您的位置:首页 > 其它

shiro认证的流程

2016-04-28 22:56 309 查看
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整合来实现用户的认证
    

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

  
<!--
fitler工厂,可以创建不同的 过虑器 -->
     <bean
id="shiroFilter"
class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"
>
          
<property
name=
"securityManager"
ref="securityManager"
/>
          
<!-- loginUrl会被formAuthenticationFilter判断是不是认证提交 -->
          
<property
name=
"loginUrl"
value ="/login.action"
/>
          
<!-- 认证成功统一跳转到first.action,建议不配置,
shiro认证成功自动到上一个请求路径 -->
          
<property
name=
"successUrl"
value="/first.action"
/>
          
<property
name=
"unauthorizedUrl"
value="/refuse.jsp"
/>
          
<!-- 自定义认证filter配置 -->
          
<property
name=
"filters">
              
<map>
                   
<!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中 -->
                   
<entry
key=
"authc"
value-ref="formAuthenticationFilter"
/>
              
</map>
          
</property>
          
<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
          
<property
name=
"filterChainDefinitions"
>
              
<value>
                   
<!-- 对静态资源设置匿名访问 -->
[align=left]                   /images/** = anon[/align]
                   /
js/** = anon
[align=left]                   /styles/** = anon[/align]
                   /
jfreechart = anon
[align=left]                   /test/** =anon[/align]
                   
<!-- 验证码,可匿名访问 -->
[align=left]                   /validatecode.jsp = anon[/align]
[align=left]
[/align]
                   
<!-- 退出拦截,请求logout.action执行退出操作 -->
[align=left]                   /logout.action = logout[/align]
[align=left]
[/align]
                   
<!-- /** = authc 所有url都必须认证通过才可以访问 -->
                   /** =
authc
              
</value>
          
</property>
     </bean
>
 
理解FormAuthenticationFilter执行流程(重点理解)
     
[align=left]    loginUrl会被formAuthenticationFilter判断是不是认证提交[/align]

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
   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.");
[align=left]                }[/align]
               
return
executeLogin(request, response);
            }
else
{
               
if
(log
.isTraceEnabled()) {
                   
log.trace("Login page view."
);
[align=left]                }[/align]
               
//allow them to see the login page ;)
               
return
true ;
[align=left]            }[/align]
        }
else
{
           
if
(log
.isTraceEnabled()) {
               
log.trace("Attempting to access a path which requires authentication.  Forwarding to the "
+
                       
"Authentication url ["
+ getLoginUrl() + "]"
);
[align=left]            }[/align]
[align=left]
[/align]
[align=left]            saveRequestAndRedirectToLogin(request, response);[/align]
           
return
false ;
[align=left]        }[/align]
 
    
 2.自定义的realm--->他是吧用户输入的账号和密码   和 调用realm去数据库中查到的用户名和密码进行比对  。存在的话给认证器返回查到的信息。否则返回null
  
public
class
customRealm  extends
AuthorizingRealm{
[align=left]     [/align]
[align=left]     @Override[/align]
     public
boolean
supports(AuthenticationToken token) {
          
return
token instanceof
  UsernamePasswordToken;
[align=left]     }[/align]
[align=left]
[/align]
[align=left]     @Override[/align]
     public
String getName() {
          
return
"customRealm" ;
[align=left]     }[/align]
[align=left]
[/align]
[align=left]     @Autowired[/align]
     private
ServiceFacade
serviceFacade;
[align=left]     [/align]
     protected
AuthenticationInfo doGetAuthenticationInfo(
              AuthenticationToken token)
throws
AuthenticationException {
           
//首先获取用户输入的账号。根据账号去数据库中查用户。对用户做出判断
[align=left]            String usercode = (String) token.getPrincipal();[/align]
           
//根据 usercode查询用户
            SysUser sysUser =
serviceFacade.getSysUserService().findSysUserByUserCode(usercode);
           
if
(sysUser==null) {
              
return
null ;
[align=left]          }[/align]
[align=left]            String pwd = sysUser.getPwd();[/align]
            ActiveUser activeUser =
serviceFacade.getSysUserService().createUser(usercode);
            SimpleAuthenticationInfo simpleAuthenticationInfo =
new
SimpleAuthenticationInfo(activeUser,pwd, getName());
          
return
simpleAuthenticationInfo;
[align=left]     }[/align]
[align=left]     [/align]
[align=left] [/align]
最后,是登录的login.action的实现:
1、能够返回一个登陆页面

2、接收post提交

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

[align=left]// 返回一个登陆页面[/align]

          
return
"login" ;
   注意:
解决页面的验证码的问题----》自定义一个类 继承FormAuthenticationFilter这个拦截器  在执行认证之前,加入对验证码的校验
         
protected
boolean
onAccessDenied(ServletRequest request,
              ServletResponse response)
throws
Exception {
          
// 首先将ServletRequest强转成 httprequest
[align=left]          HttpServletRequest httpServletRequest = (HttpServletRequest) request;[/align]
[align=left]
[/align]
          
if
(isLoginRequest(request, response)) {// 判读是否是认证的请求 .是认证请求
              
if
(isLoginSubmission(request, response)) {// 是否是post请求
                   
if
(log
.isTraceEnabled()) {
                        
log.trace("Login submission detected.  Attempting to execute login.");
[align=left]                   }[/align]
[align=left]
[/align]
[align=left]                   HttpSession session = httpServletRequest.getSession();[/align]
                   
// 这个是正确的验证码
[align=left]                   String validateCode = (String) session[/align]
                             .getAttribute(
"validateCode");
                   
// 获得页面输入的验证码
[align=left]                   String randomCode = httpServletRequest[/align]
                             .getParameter(
"validateCode");
                   
// 判断
                   
if
(randomCode != null
&& validateCode !=
null
[align=left]                             && !randomCode.equals(validateCode)) {[/align]
                        
// 校验失败的话,通过shiroLoginFailure设置到request中
                        httpServletRequest.setAttribute(
"shiroLoginFailure",
                                  
"randomCodeError");
                        
// 转发到登陆页面
                        
return
true ;
[align=left]                   }[/align]
[align=left]
[/align]
                   
return
executeLogin(request, response);// 执行认证,执行认证之前,加入验证码的认证

 
此时认证成功后页面不会发生跳转---》解决
  
[align=left]// 解决页面跳转的问题[/align]
     protected
boolean
onLoginSuccess(AuthenticationToken token,

[align=left]              Subject subject, ServletRequest request, ServletResponse response)[/align]
              
throws
Exception {

[align=left]          HttpServletRequest httpServletRequest = (HttpServletRequest) request;[/align]
          
// 如果是 ajax请求的话让它输出 json数据,否则执行原来的方法进行重定向

          
if ("XMLHttpRequest"
.equalsIgnoreCase(httpServletRequest

                   .getHeader(
"X-Requested-With"))) {// 是ajax请求

              
// 输出 json

              
// ajax 请求

              response.setCharacterEncoding(
"utf-8");

               response.setContentType(
"application/json;charset=utf-8"
);

[align=left]              response.getWriter()[/align]
                        .write(
"{\"resultInfo\":{\"type\":\"1\",\"messageCode\":\"906\",\"message\":\"登陆成功\"}}"
);

              
return
false ;

          }
else
{

              
// 使用原来的代码进行重定向

[align=left]              issueSuccessRedirect(request, response);[/align]
[align=left][/align]
              
return
false ;

[align=left]          }[/align]
[align=left][/align]

[align=left]     }[/align]
       
   
[align=justify]在applicationContext-shiro.xml中  配置authc为CustomFormAuthenticationFilter[/align]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: