zoukankan      html  css  js  c++  java
  • 将 Shiro 作为应用的权限基础 二:基于SpringMVC实现的认证过程

    认证就是验证用户身份的过程。在认证过程中,用户需要提交实体信息(Principals)和凭据信息(Credentials)以检验用户是否合法。最常见的“实体/凭证”组合便是“用户名/密码”组合。 

     

    一、认证过程 

     

    1、收集实体/凭据信息 

    Java代码  

     

        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        token.setRememberMe(true);  

    UsernamePasswordToken支持最常见的用户名/密码的认证机制。同时,由于它实现了RememberMeAuthenticationToken接口,我们可以通过令牌设置“记住我”的功能。 

    但是,“已记住”和“已认证”是有区别的: 

    已记住的用户仅仅是非匿名用户,你可以通过subject.getPrincipals()获取用户信息。但是它并非是认证通过的用户,当你访问需要认证用户的功能时,你仍然需要重新提交认证信息。 

    这一区别可以参考淘宝网站,网站会默认记住登录的用户,再次访问网站时,对于非敏感的页面功能,页面上会显示记住的用户信息,但是当你访问网站账户信息时仍然需要再次进行登录认证。 

     

    2、提交实体/凭据信息 

    Java代码  

        

    Subject currentUser = SecurityUtils.getSubject();  
    currentUser.login(token); 

     

    收集了实体/凭据信息之后,我们可以通过SecurityUtils工具类,获取当前的用户,然后通过调用login方法提交认证。 

     

    3、认证

    如果我们自定义Realm实现,比如我后面的例子中,自定义了ShiroDbRealm类,当执行currentUser.login(token)时,会先执行ShiroDbRealm.doGetAuthenticationInfo()进行登录认证。

        

    /**
         * 验证当前登录的Subject
         * @see经测试:本例中该方法的调用时机为LoginController.login()方法中执行Subject.login()时
         */ 
    protectedAuthenticationInfo doGetAuthenticationInfo(
    AuthenticationTokenauthcToken) throws AuthenticationException {
    //获取基于用户名和密码的令牌 
           //实际上这个authcToken是从LoginController里面currentUser.login(token)传过来的 
    UsernamePasswordTokentoken = (UsernamePasswordToken) authcToken;
     
    //从数据库中查询用户用信息
    Useruser = userService.getByAccount(token.getUsername());
    if(user != null) {
     //此处无需比对,比对的逻辑Shiro会做,我们只需返回一个和令牌相关的正确的验证信息 
    returnnew SimpleAuthenticationInfo(user.getAccount(), user
    .getPassword(),getName());
    }else {
     //没有返回登录用户名对应的SimpleAuthenticationInfo对象时,就会在LoginController中抛出UnknownAccountException异常 
    returnnull;
    }
    }

     

     

     

    4、认证处理 

    Java代码  

    收藏代码

        

    try {  
        currentUser.login(token);  
    } catch ( UnknownAccountException uae ) { ...  
    } catch ( IncorrectCredentialsException ice ) { ...  
    } catch ( LockedAccountException lae ) { ...  
    } catch ( ExcessiveAttemptsException eae ) { ...  
    } ... catch your own ...  
    } catch ( AuthenticationException ae ) {  
        //unexpected error?  
    }  

     

    如 果login方法执行完毕且没有抛出任何异常信息,那么便认为用户认证通过。之后在应用程序任意地方调用 SecurityUtils.getSubject()都可以获取到当前认证通过的用户实例,使用subject.isAuthenticated()判 断用户是否已验证都将返回true. 

    相反,如果login方法执行过程中抛出异常,那么将认为认证失败。Shiro有着丰富的层次鲜明的异常类来描述认证失败的原因,如代码示例。 

     

    二、登出操作 

    登出操作可以通过调用subject.logout()来删除你的登录信息,如: 

    Java代码  

    currentUser.logout();//removes all identifying information and invalidates their session too.

    当执行完登出操作后,Session信息将被清空,subject将被视作为匿名用户。 

     

    三、认证内部处理机制 

    以上,是Shiro认证在应用程序中的处理过程,下面将详细解说Shiro认证的内部处理机制。 


     

     

    如上图,我们通过Shiro架构图的认证部分,来说明Shiro认证内部的处理顺序: 

    1、应用程序构建了一个终端用户认证信息的AuthenticationToken实例后,调用Subject.login方法。 

    2、Sbuject会委托应用程序设置的securityManager实例调用securityManager.login(token)方法。 


    3、 SecurityManager接受到token(令牌)信息后会委托内置的Authenticator的实例(通常都是 ModularRealmAuthenticator类的实例)调用 authenticator.authenticate(token).ModularRealmAuthenticator在认证过程中会对设置的一个 或多个Realm实例进行适配,它实际上为Shiro提供了一个可拔插的认证机制。

     

    4、 如果在应用程序中配置了多个Realm,ModularRealmAuthenticator会根据配置的 AuthenticationStrategy(认证策略)来进行多Realm的认证过程。在Realm被调用 后,AuthenticationStrategy将对每一个Realm的结果作出响应。 

    注:如果应用程序中仅配置了一个Realm,Realm将被直接调用而无需再配置认证策略。 


    5、Realm将调用getAuthenticationInfo(token);getAuthenticationInfo方法就是实际认证处理,我们通过覆盖Realm的doGetAuthenticationInfo方法来编写我们自定义的认证处理。 

     

     

    四、使用多个Realm的处理机制: 

    AuthenticationStrategy(认证策略) 

    当应用程序配置了多个Realm时,ModularRealmAuthenticator将根据认证策略来判断认证成功或是失败。 

    例 如,如果只有一个Realm验证成功,而其他Realm验证失败,那么这次认证是否成功呢?如果大多数的Realm验证成功了,认证是否就认为成功呢?或 者,一个Realm验证成功后,是否还需要判断其他Realm的结果?认证策略就是根据应用程序的需要对这些问题作出决断。 

    认证策略是一个无状态的组件,在认证过程中会经过4次的调用: 

    • 在所有Realm被调用之前
    • 在调用RealmgetAuthenticationInfo方法之前
    • 在调用RealmgetAuthenticationInfo方法之后
    • 在所有Realm被调用之后

    认证策略的另外一项工作就是聚合所有Realm的结果信息封装至一个AuthenticationInfo实例中,并将此信息返回,以此作为Subject的身份信息。 

    Shiro有3中认证策略的具体实现: 

     

    AtLeastOneSuccessfulStrategy

    只要有一个(或更多)的Realm验证成功,那么认证将被视为成功

    FirstSuccessfulStrategy

    第一个Realm验证成功,整体认证将被视为成功,且后续Realm将被忽略

    AllSuccessfulStrategy

    所有Realm成功,认证才视为成功

    ModularRealmAuthenticator内置的认证策略默认实现是AtLeastOneSuccessfulStrategy 方式,因为这种方式也是被广泛使用的一种认证策略。

     

    五、认证的代码示例

    LoginController:处理登录请求的Controller类

    <span style="font-size:18px">packageorg.shiro.demo.controller;
     
    importjava.awt.Color;
    importjava.awt.image.BufferedImage;
    importjava.io.IOException;
     
    importjavax.imageio.ImageIO;
    importjavax.servlet.http.HttpServletRequest;
    importjavax.servlet.http.HttpServletResponse;
    importjavax.servlet.http.HttpSession;
     
    importorg.apache.commons.lang.StringUtils;
    importorg.apache.shiro.SecurityUtils;
    importorg.apache.shiro.authc.AuthenticationException;
    importorg.apache.shiro.authc.UsernamePasswordToken;
    importorg.apache.shiro.subject.Subject;
    importorg.apache.shiro.web.util.WebUtils;
    importorg.shiro.demo.entity.User;
    importorg.shiro.demo.util.ValidateCode;
    importorg.springframework.stereotype.Controller;
    importorg.springframework.web.bind.annotation.RequestMapping;
    importorg.springframework.web.bind.annotation.RequestMethod;
     
    @Controller
    publicclass LoginController {
     
    @RequestMapping(value= "/login")
    publicString login(User user,HttpSession session, HttpServletRequest request){
    //判断验证码
    Stringcode = (String) session.getAttribute("validateCode");
    StringsubmitCode = WebUtils.getCleanParam(request, "validateCode");
     
    if(StringUtils.isEmpty(submitCode) ||!StringUtils.equals(code,submitCode.toLowerCase())) {
    return"redirect:/";
    }
     
    //获取当前的Subject 
    SubjectcurUser = SecurityUtils.getSubject();
    UsernamePasswordTokentoken = new UsernamePasswordToken(user.getAccount(),user.getPassword());
    token.setRememberMe(true);
    try {
     //在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm执行必须的认证检查 
               //每个Realm都能在必要时对提交的AuthenticationTokens作出反应 
               //所以这一步在调用login(token)方法时,它会走到ShiroDbRealm.doGetAuthenticationInfo()方法中
    curUser.login(token);
     
    return"/system/main";
    }catch(AuthenticationException e) {
    //通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景
    token.clear();
    return"redirect:/";
    }
    }
     
    }</span>



     

     

    ShiroDbRealm:

  • 相关阅读:
    Golang Failpoint 的设计与实现
    没涉及到最值求解;观点:矩阵乘法无法表达出结果。 现实生活中事件、现象的数学表达
    多元微分学 枚举破解15位路由器密码 存储空间限制 拆分减长,求最值 数据去重
    ARP Poisoning Attack and Mitigation Techniques ARP欺骗 中间人攻击 Man-In-The-Middle (MITM) attack 嗅探 防范 Can one MAC address have two different IP addresses within the network?
    The C10K problem
    HTTP Streaming Architecture HLS 直播点播 HTTP流架构
    现代IM系统中消息推送和存储架构的实现
    现代IM系统中的消息系统架构
    长连接锁服务优化实践 C10K问题 nodejs的内部构造 limits.conf文件修改 sysctl.conf文件修改
    doubleclick cookie、动态脚本、用户画像、用户行为分析和海量数据存取 推荐词 京东 电商 信息上传 黑洞 https://blackhole.m.jd.com/getinfo
  • 原文地址:https://www.cnblogs.com/hanxue112253/p/3850540.html
Copyright © 2011-2022 走看看