shiro认证的流程

来源:互联网 发布:linux从文件命令变量 编辑:程序博客网 时间:2024/04/28 15:30
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.    
      <!--shiro过虑器,DelegatingFilterProxy通过代理模式通过spring容器中的filter工厂创建真正的 fitler -->

           <filter>

                 <filter-name>shiroFilter</filter-name>

                 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

                 <!-- 设置true由 servlet容器控制filter的生命周期 -->

                 <init-param>

                      <param-name>targetFilterLifecycle</param-name>

                     <param-value>true</param-value>

                 </init-param>

                 <!-- 设置spring容器filter工厂的bean id,如果不设置则找与filter-name一致的bean-->

                 <init-param>

                     <param-name>targetBeanName</param-name>

                     <param-value>shiroFilter</param-value>

                 </init-param>

           </filter>

           <filter-mapping>

                 <filter-name>shiroFilter</filter-name>

                 <url-pattern>/*</url-pattern>

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

  
<!--fitler工厂,可以创建不同的 过虑器 -->
     <beanid="shiroFilter"class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
          <propertyname="securityManager"ref="securityManager"/>
          <!-- loginUrl会被formAuthenticationFilter判断是不是认证提交 -->
          <propertyname="loginUrl" value ="/login.action"/>
          <!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
          <propertyname="successUrl" value="/first.action"/>
          <propertyname="unauthorizedUrl"value="/refuse.jsp"/>
          <!-- 自定义认证filter配置 -->
          <propertyname="filters">
              <map>
                   <!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中 -->
                   <entrykey="authc" value-ref="formAuthenticationFilter"/>
              </map>
          </property>
          <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
          <propertyname="filterChainDefinitions">
              <value>
                   <!-- 对静态资源设置匿名访问 -->
                   /images/** = anon
                   /js/** = anon
                   /styles/** = anon
                   /jfreechart = anon
                   /test/** =anon
                   <!-- 验证码,可匿名访问 -->
                   /validatecode.jsp = anon

                   <!-- 退出拦截,请求logout.action执行退出操作 -->
                   /logout.action = logout

                   <!-- /** = authc 所有url都必须认证通过才可以访问 -->
                   /** =authc
              </value>
          </property>
     </bean>
 
理解FormAuthenticationFilter执行流程(重点理解)
     
    loginUrl会被formAuthenticationFilter判断是不是认证提交

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
   1.   formAuthentivationFilter--->底层代码实现  。他的执行流程就是上面那张图的执行流程
         
                                 
protectedbooleanonAccessDenied(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
  
publicclass customRealm  extendsAuthorizingRealm{
     
     @Override
     publicbooleansupports(AuthenticationToken token) {
          return token instanceof  UsernamePasswordToken;
     }

     @Override
     publicString getName() {
          return "customRealm" ;
     }

     @Autowired
     privateServiceFacade serviceFacade;
     
     protectedAuthenticationInfo 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、接收到认证失败的原因
 
     publicString 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这个拦截器  在执行认证之前,加入对验证码的校验
         
protectedbooleanonAccessDenied(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);// 执行认证,执行认证之前,加入验证码的认证
 
此时认证成功后页面不会发生跳转---》解决
  
// 解决页面跳转的问题
     protectedboolean 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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0 0
原创粉丝点击