shiro认证的流程

3.1shiro认证流程(掌握!!!)

   1、subject.login(token);
    token:令牌,包括账号和密码
    token是根据当前用户请求的账号和密码去构造!
    2、securityManager.login(token)
    3、Authenticator.login(token) (重点理解部分)
     Authenticator获取到用户输入的账号和密码。
     Authenticator去调用realm从数据源中获取正确的账号和密码。
     realm:在没有自定义realm时候,shiro使用自定义的CustomRealm。
     CustomRealm通过token获取请求的账号,根据账号去查询数据。


    如果CustomRealm根据用户请求的账号从数据库没有找到记录,CustomRealm给认证器返回NULL,认证器抛出异常UnknownAccountException (账号不存在)
    如果CustomRealm根据用户请求的账号从数据库找到记录了,将账号对应的密码 给认证器返回,认证器拿realm返回的正确的密码 和token中输入的密码进行比对,如果一致则认证通过,否则抛出异常 IncorrectCredentialsException(密码 错误)

shiro框架的方式和spring整合来实现用户的认证
    
    1.   web.xml中配置一个代理的对象。他会通过spring容器找到filter的工厂来找到真正的filter.    


           <filter >

                 <filter-name> shiroFilterfilter-name >

                 <filter-class> org.springframework.web.filter.DelegatingFilterProxyfilter-class >

                

                 <init-param>

                      <param-name> targetFilterLifecycleparam-name >

                     <param-value> true param-value>

                 init-param>

                

                 <init-param>

                     <param-name> targetBeanNameparam-name >

                     <param-value> shiroFilterparam-value >

                 init-param>

           filter >

           <filter-mapping >

                 <filter-name> shiroFilterfilter-name >

                 <url-pattern> /* url-pattern>

           filter-mapping >
       
    2. applicationContext-shiro.xml中,有很多的filter。都是按顺序执行的 : anno(公开的放行    logout-->退出  auth-->必须通过认证)

  
     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" >
           <property name= "securityManager" ref="securityManager" />
          
           <property name= "loginUrl" value ="/login.action" />
          
           <property name= "successUrl" value="/first.action" />
           <property name= "unauthorizedUrl" value="/refuse.jsp" />
          
           <property name= "filters">
               <map>
                   
                    <entry key= "authc" value-ref="formAuthenticationFilter" />
               map>
           property>
          
           <property name= "filterChainDefinitions" >
               <value>
                   
                   /images/** = anon
                   / js/** = anon
                   /styles/** = anon
                   / jfreechart = anon
                   /test/** =anon
                   
                   /validatecode.jsp = anon

                   
                   /logout.action = logout

                   
                   /** = authc
               value>
           property>
     bean >
 
理解FormAuthenticationFilter执行流程(重点理解)
     
    loginUrl会被formAuthenticationFilter判断是不是认证提交

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
   1.   formAuthentivationFilter--->底层代码实现  。他的执行流程就是上面那张图的执行流程
         
                                 
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (isLoginRequest(request, response)) {
            if (isLoginSubmission(request, response)) {
                if (log .isTraceEnabled()) {
                    log.trace("Login submission detected.  Attempting to execute login.");
                }
                return executeLogin(request, response);
            } else {
                if (log .isTraceEnabled()) {
                    log.trace("Login page view." );
                }
                //allow them to see the login page ;)
                return true ;
            }
        } else {
            if (log .isTraceEnabled()) {
                log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +
                        "Authentication url [" + getLoginUrl() + "]" );
            }

            saveRequestAndRedirectToLogin(request, response);
            return false ;
        }
 
    
 2.自定义的realm--->他是吧用户输入的账号和密码   和 调用realm去数据库中查到的用户名和密码进行比对  。存在的话给认证器返回查到的信息。否则返回null
  
public class customRealm  extends AuthorizingRealm{
     
     @Override
     public boolean supports(AuthenticationToken token) {
           return token instanceof   UsernamePasswordToken;
     }

     @Override
     public String getName() {
           return "customRealm" ;
     }

     @Autowired
     private ServiceFacade serviceFacade;
     
     protected AuthenticationInfo doGetAuthenticationInfo(
              AuthenticationToken token) throws AuthenticationException {
            //首先获取用户输入的账号。根据账号去数据库中查用户。对用户做出判断
            String usercode = (String) token.getPrincipal();
            //根据 usercode查询用户
            SysUser sysUser = serviceFacade.getSysUserService().findSysUserByUserCode(usercode);
            if (sysUser==null) {
               return null ;
          }
            String pwd = sysUser.getPwd();
            ActiveUser activeUser = serviceFacade.getSysUserService().createUser(usercode);
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(activeUser,pwd, getName());
           return simpleAuthenticationInfo;
     }
     
 

  1. 最后,是登录的login.action的实现:
    1、能够返回一个登陆页面

    2、接收post提交

    3、接收到认证失败的原因
 
      public String login() {
          HttpServletRequest request = this.getRequest();
           // shiro 在认证过程中出现错误后将异常类路径通过request返回
          String exceptionClassName = (String) request
                   .getAttribute( "shiroLoginFailure");
           if (exceptionClassName != null) {
               if (UnknownAccountException.class .getName().equals(
                        exceptionClassName)) {
                    // 账号不存在
                    // 抛出异常
                   ResultInfo resultInfo = ResultUtil.createFail(Config.MESSAGE,
                             101, null);
                    throw new ExceptionResultInfo(resultInfo);
              } else if (IncorrectCredentialsException.class .getName().equals(
                        exceptionClassName)) {
                    // 用户名或密码 错误
                   ResultInfo resultInfo = ResultUtil.createFail(Config.MESSAGE,
                             114, null);
                    throw new ExceptionResultInfo(resultInfo);
              } else if ("randomCodeError" .equals(exceptionClassName)) {
                    // 提供验证码错误
                   ResultInfo resultInfo = ResultUtil.createFail(Config.MESSAGE,
                             113, null);
                    throw new ExceptionResultInfo(resultInfo);
              } else {
                    // 最终在异常处理器生成未知错误
                   ResultInfo resultInfo = ResultUtil.createFail(Config.MESSAGE,
                             900, null);
                    throw new ExceptionResultInfo(resultInfo);
              }
          }
// 返回一个登陆页面
           return "login" ;

   注意:
解决页面的验证码的问题----》 自定义一个类 继承FormAuthenticationFilter这个拦截器  在执行认证之前,加入对验证码的校验
         
protected boolean onAccessDenied(ServletRequest request,
              ServletResponse response) throws Exception {
           // 首先将ServletRequest强转成 httprequest
          HttpServletRequest httpServletRequest = (HttpServletRequest) request;

           if (isLoginRequest(request, response)) {// 判读是否是认证的请求 .是认证请求
               if (isLoginSubmission(request, response)) {// 是否是post请求
                    if (log .isTraceEnabled()) {
                         log.trace("Login submission detected.  Attempting to execute login.");
                   }

                   HttpSession session = httpServletRequest.getSession();
                    // 这个是正确的验证码
                   String validateCode = (String) session
                             .getAttribute( "validateCode");
                    // 获得页面输入的验证码
                   String randomCode = httpServletRequest
                             .getParameter( "validateCode");
                    // 判断
                    if (randomCode != null && validateCode != null
                             && !randomCode.equals(validateCode)) {
                         // 校验失败的话,通过shiroLoginFailure设置到request中
                        httpServletRequest.setAttribute( "shiroLoginFailure",
                                   "randomCodeError");
                         // 转发到登陆页面
                         return true ;
                   }

                    return executeLogin(request, response);// 执行认证,执行认证之前,加入验证码的认证
 
此时认证成功后页面不会发生跳转---》解决
  
// 解决页面跳转的问题
     protected boolean onLoginSuccess(AuthenticationToken token,
              Subject subject, ServletRequest request, ServletResponse response)
               throws Exception {
          HttpServletRequest httpServletRequest = (HttpServletRequest) request;
           // 如果是 ajax请求的话让它输出 json数据,否则执行原来的方法进行重定向
           if ("XMLHttpRequest" .equalsIgnoreCase(httpServletRequest
                   .getHeader( "X-Requested-With"))) {// 是ajax请求
               // 输出 json
               // ajax 请求
              response.setCharacterEncoding( "utf-8");
               response.setContentType( "application/json;charset=utf-8" );
              response.getWriter()
                        .write( "{\"resultInfo\":{\"type\":\"1\",\"messageCode\":\"906\",\"message\":\"登陆成功\"}}" );
               return false ;
          } else {
               // 使用原来的代码进行重定向
              issueSuccessRedirect(request, response);

               return false ;
          }

     }
       
   
在applicationContext-shiro.xml中  配置authc为CustomFormAuthenticationFilter
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

你可能感兴趣的:(javaWeb)