当前位置: 首页 > 编程开发 > Java教程 >正文

我是这样用shiro的

来源:互联网时间:2017-05-15 17:23:40编辑:网友分享
我在shiro学习上花费了一些时间,shiro的资料网上一大推,之前自己学习的知识点一直记录在有道云笔记上,有道云有自己的好处 那就是没有网络的时候依然可以记录一些东西,但是弊端

我在shiro学习上花费了一些时间,shiro的资料网上一大推,之前自己学习的知识点一直记录在有道云笔记上,有道云有自己的好处 那就是没有网络的时候依然可以记录一些东西,但是弊端就是不能与大家一起分享和讨论,最后还是选择在segmentFault,一上来发现自己声望值是负数,有点小悲伤啊,以后学习到东西后自己会在这里写写记记,一是归纳梳理知识且自己记性不好,方便以后自我回忆,二是希望能和大家讨论,我有不对的地方希望能有大神指点。废话不多说,写一下我最近学习的shiro的用法。


正文

blob.png

大体的登录认证流程如上图,当进入登录页面后,会先进入通过shiroFilter安全认证过滤器,然后读取数据库信息来进行登录和授权的认证,这一部分是交给realm来进行的,当认证成功后会跳转到successURL对应的地址中去如果失败会跳转到loginUrl中。图上的的${adminPath}只是从配置文件properties中读出配置参数罢了,可以把它肯成/a。


web.xml配置

    <!-- Apache Shiro -->
   <filter>
       <filter-name>shiroFilter</filter-name>
       <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
       <init-param>
           <param-name>targetFilterLifecycle</param-name>
           <param-value>true</param-value>
       </init-param>
   </filter>
   <filter-mapping>
       <filter-name>shiroFilter</filter-name>
       <url-pattern>/*</url-pattern>
   </filter-mapping>

web.xml基本上是固定模板,/*拦截了所有的请求,但是记住下shiroFilter这个名字。


spring-context-shiro.xml 配置

<description>Shiro Configuration</description>

   <!-- 加载配置属性文件 -->
   <context:property-placeholder ignore-unresolvable="true" location="classpath:jeesite.properties" />
   
   <!-- (会被引入)Shiro权限过滤过滤器定义 -->
   <bean name="shiroFilterChainDefinitions" class="java.lang.String">
       <constructor-arg>
           <value>
               /static/** = anon
               /userfiles/** = anon
               ${adminPath}/login = authc
               ${adminPath}/logout = logout
               ${adminPath}/sys/** = roles[sys]
               ${adminPath}/cms/** = perms[cms:view]
               ${adminPath}/** = user            </value>
       </constructor-arg>
   </bean>
   
   <!-- (扩展)安全认证过滤器 -->
   <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
       <property name="securityManager" ref="securityManager" />
       <property name="loginUrl" value="${adminPath}/login" />
       <property name="successUrl" value="${adminPath}/success" />
       <property name="unauthorizedUrl" value="/unauthorized.jsp" />
       <property name="filters">
           <map>
               <entry key="authc" value-ref="formAuthenticationFilter"/>
           </map>
       </property>
       <property name="filterChainDefinitions">
           <ref bean="shiroFilterChainDefinitions"/>
       </property>
   </bean>
   
   <!-- (扩展)定义Shiro安全管理配置 -->
   <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
       <!-- 通过realm来读取身份信息和授权信息 -->
       <property name="realm" ref="systemAuthorizingRealm" />
   </bean>
   
   <!-- (固定)保证实现了Shiro内部lifecycle函数的bean执行 -->
   <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
   
   <!--(固定) AOP式方法级权限检查  -->
   <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
       <property name="proxyTargetClass" value="true" />
   </bean>
   <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
       <property name="securityManager" ref="securityManager"/>
   </bean>

XML配置文件中很多也是固定不变的,官网文档也有介绍,anon表示请求对应的路径不认证,authc表示请求对应的路径需要登录认证,roles[sys]表示请求对应的路径需要角色为sys的才允许,perms[cms:view]表示请求对应的路径需要有cms:view权限的才允许。

<property name="loginUrl" value="${adminPath}/login" />

表示登录失败后会跳转到对应请求

<property name="successUrl" value="${adminPath}/success" />

表示登录成功后会跳转的请求

<property name="unauthorizedUrl" value="/unauthorized.jsp" />

表示访问了无权访问的链接后跳转的请求。

<entry key="authc" value-ref="formAuthenticationFilter"/>

表示 authc这个会通过FormAuthenticationFilter这个类来验证

<property name="realm" ref="systemAuthorizingRealm" />

这是我们自定义的realm来认证登录和授权信息的

blob.png

实验的目录结构,如上图

blob.png


重写FormAuthenticationFilter类

@Servicepublic class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter{    
   public static final String DEFAULT_CAPTCHA_PARAM = "validateCode";    
   private String captchaParam = DEFAULT_CAPTCHA_PARAM;

   @Override    protected AuthenticationToken createToken(ServletRequest request,ServletResponse response) {
       System.out.println("-------------进入创建token方法-------------");        // TODO Auto-generated method stub
       String username = getUsername(request);        String password = getPassword(request);        if(password == null){
           password = "";
       }        String captcha = getCapcha(request);
       System.out.println("-------------出创建token方法-------------");        return new UsernamePasswordToken(username,password,captcha);
   }    
   
   public String getCapcha(ServletRequest request){        return WebUtils.getCleanParam(request, captchaParam);
   }    
   public String getSuccessUrl(){        return super.getSuccessUrl();
   }
   
   @Override    protected void issueSuccessRedirect(ServletRequest request,ServletResponse response) throws Exception {        // TODO Auto-generated method stub、
       System.out.println("-------------issueSuccessRedirect-------------");
       WebUtils.issueRedirect(request, response, getSuccessUrl());
   }


   @Override    protected boolean onLoginFailure(AuthenticationToken token,AuthenticationException e, ServletRequest request,
           ServletResponse response) {        // TODO Auto-generated method stub
       System.out.println("-------------onLoginFailure-------------");        String className = e.getClass().getName();        String message = "";
       System.out.println("=========e.getCLass().getName()===========:"+className);        if(IncorrectCredentialsException.class.getName().equals(className)
               || UnknownAccountException.class.getName().equals(className)){
           message = "用户名或密码错误";
       }else{
           message = "系统出现点问题,请稍后再试!";
           e.printStackTrace(); // 输出到控制台
       }
       request.setAttribute("message",message);        return true;
   }

}

当我们输入完用户密码后需要进入一个 shiroFilter 安全认证过滤器,这里为什么需要重写了呢,其实本质是shiro已经给我们提供了相应的 FormAuthenticationFilter 类,可以在其中根据用户输入的信息创建 token 来供以后流程的认证,但是如果我们还想加入一些其他的东西来一起创建这个 token,比如说我们通过用户名、密码、验证码、是否记住我、是否是手机端登录等等信息来一起组成一个token的时候,这个时候我们就可以重写 FormAuthenticationFilter 中的一些方法来实现了。

WebUtils.getCleanParam(request, captchaParam) 是 shiro 给我们提供的封装,其实就是 request.getParameter(paramName) 的一个封装罢了,我们可以通过它来获取前台输入的参数。

重写UsernamePasswordToken类

public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken{    
   private static final long serialVersionUID = 1L;    
   private String captcha;    
   public UsernamePasswordToken(String username,String password){        super(username,password);
   }    public UsernamePasswordToken() {        super();
   }    
   public UsernamePasswordToken(String username,String password,String captcha){        super(username,password);        this.captcha = captcha;
   }    public String getCaptcha() {        return captcha;
   }    public void setCaptcha(String captcha) {        this.captcha = captcha;
   }
   
}

通过重写UsernamePasswordToken,目的是来根据开发者想创建token的参数来构造出一个新的UsernamePasswordToken以供使用。我这里为了简化只写了用户名密码参数。

此时此刻我们就有了token,下面开始我们的认证啦~

通过自定义的 Realm 来认证授权

@Servicepublic class SystemAuthorizingRealm extends AuthorizingRealm{    
   @Autowired
   private SystemService systemService;    /**
    * 登录认证
    */
   @Override
   protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {        // TODO Auto-generated method stub
       System.out.println("-------------进入登录认证方法-------------");
       UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
       String username = token.getUsername();
       User user = systemService.getUserByName(username);
       System.out.println("-------1----getName()--------:"+getName());        if(user != null){
           System.out.println("-------2----getName()--------:"+getName());
           System.out.println("-------------登录认证结束--返回SimpleAuthenticationInfo-------------");            return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
       }else{
           System.out.println("------------登录认证结束--返回null------------");            return null;
       }
   }    @Override
   protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {        // TODO Auto-generated method stub
       System.out.println("-------------进入授权认证方法-------------");//        Principal principal = (Principal) getAvailablePrincipal(principals);
       String name = (String) principals.getPrimaryPrincipal();//        User user = systemService.getUserByName(principal.getName());
       User user = systemService.getUserByName(name);        if(user != null){
           SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
           List<Role> Roles = systemService.getRoleByUserId(user.getId());            for(Role r:Roles){
               System.out.println("role---> "+r.getRole());
               info.addRole(r.getRole());
               List<Perm> Perms = systemService.getPermByRoleId(r.getId());                if(Perms != null && Perms.size() >0 ){                    for(Perm p:Perms){
                       System.out.println("perm---> "+p.getPermission());
                       info.addStringPermission(p.getPermission());
                   }
               }
           }
           System.out.println("-----------授权认证结束--返回info------------");            return info;
       }else{
           System.out.println("-----------授权认证结束--返回null------------");            return null;
       }
   }
}

在 realm 中我们需要继承 AuthorizingRealm,并重写两个方法:

  1. doGetAuthenticationInfo 方法:用户身份认证方法,根据参数返回 SimpleAuthenticationInfo,如果登录成功则跳转到xml配置文件中的 successUrl 地址,如果登录失败则跳转到 loginUrl 地址;

  2. doGetAuthorizationInfo 方法:身份权限认证,利用了 SimpleAuthorizationInfo,把角色和权限都给予 SimpleAuthorizationInfo 来进行授权;在是 shiro 中如果访问了无权访问的地址,则会跳转到 xml 配置文件中 unauthorizedUrl 对应的地址。

到这里基本上shiro就完毕了,当我们登录时候的顺序:

blob.png

上图为登录成功的流程

blob.png

上图为登录失败的流程

这时候 shiro 已经记住了用户的信息,当再请求路径的时候就不会继续验证了,在此输入请求拦连接的时候就会直接去找对应的 action 而不会再进入安全过滤器,所有我们想在此登录需要退出当前用户才行。如下代码可以退出当前用户,这样我们再请求登录的时候就会继续安全认证了。

SecurityUtils.getSubject().logout();

当我们登录成功后,我们用是sys角色但是没有 perms[cms:view] 权限 的用户访问 a/sys 是可以请求成功,但是访问 a/cms 是会跳转到 shiro 的 xml 配置文件的unauthorizedUrl 对应的地址,表示权限不足不可访问。

blob.png

上图为授权成功,用户有该请求权限,跳转到对应 action 中

blob.png

上图为授权失败,跳转到 unauthorized.jsp 页面,提示无权访问


上一篇: Java关键字final理解

下一篇:Java反射机制应用实践

您可能感兴趣的文章

相关阅读