WindySky / 我的shiro之旅 / 我的shiro之旅: 十四 shiro 自动登录

分享

   

我的shiro之旅: 十四 shiro 自动登录

2017-12-27  WindySky

shiro有几种状态,其中包括guest,user,authenticated。guest就是游客,authenticated就是认证后的用户,而user是介于两者之前。user并不代表用户已经成功认证,当用户上次登录时选择rememberMe,下次用户再访问时就是user状态。登录时选择rememberMe,shiro会通过一种加密方式将principal(我们理解为用户名)加密保存到cookie中。shiro可以通过这个cookie解密得到principal。所以当状态为user时,虽然并不代表用户已经通过认证,但我们却可以通过Subject拿到用户名。先贴出代码:

  1.      public void autoLogin(HttpServletRequest request, HttpServletResponse response) {  
  2.   
  3. Subject subject = SecurityUtils.getSubject();  
  4. if(subject.isRemembered()){  
  5.     String username = ShiroSecurityHelper.getCurrentUsername();  
  6.     LOG.info("用户【{}】自动登录----{}", username,TimeHelper.getCurrentTime());  
  7.     User user = userService.getByUsername(username);  
  8.     baseLogin(user, request, response);  
  9.     ShiroAuthorizationHelper.clearAuthorizationInfo(username); // 用户是自动登录,首先清一下用户权限缓存,让重新加载  
  10. }  


  1. public void baseLogin(User user, HttpServletRequest request, HttpServletResponse response) {    
  2.             
  3.     try {    
  4.         Subject subject= SecurityUtils.getSubject();    
  5.         if (subject.isAuthenticated()) {    
  6.             return;    
  7.         }    
  8.         //如果用户已登录,先踢出    
  9.         ShiroSecurityHelper.kickOutUser(user.getUsername());    
  10.                 
  11.         boolean rememberMe = ServletRequestUtils.getBooleanParameter(request, "rememberMe", false);    
  12.         UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword(), rememberMe);    
  13.         subject.login(token); // 登录    
  14.     } catch (Exception e) {    
  15.         //做一些异常处理    
  16.     }finally{    
  17.         ShiroAuthorizationHelper.clearAuthorizationInfo(sessionUser.getUsername());    
  18.     }    
  19. }    

ShiroAuthorizationHelper在文章我的shiro之旅: 九 shiro 清理缓存的权限信息有介绍。我们来看看DelegatingSubject类的isRemembered()方法的实现。

  1. public boolean isRemembered() {  
  2.     PrincipalCollection principals = getPrincipals();  
  3.     return principals != null && !principals.isEmpty() && !isAuthenticated();  
  4. }  
当能拿到principals和用户没认证的时候,这个方法才返回true,principals可以通过cookie解密得到。如果用户是认证状态的,这个方法将返回false。

如此看来,shiro的自动登录实现还是比较简单。

securityManager有个rememberMeManager,用来管理rememberMe的,默认情况下,这个cookie会保存一年时间,我们可以看一下RememberMeManager接口的实现类org.apache.shiro.web.mgt.CookieRememberMeManager的源码:

  1. public class CookieRememberMeManager extends AbstractRememberMeManager {  
  2.   
  3.     //TODO - complete JavaDoc  
  4.   
  5.     private static transient final Logger log = LoggerFactory.getLogger(CookieRememberMeManager.class);  
  6.   
  7.     /** 
  8.      * The default name of the underlying rememberMe cookie which is {@code rememberMe}. 
  9.      */  
  10.     public static final String DEFAULT_REMEMBER_ME_COOKIE_NAME = "rememberMe";  
  11.   
  12.     private Cookie cookie;  
  13.   
  14.     /** 
  15.      * Constructs a new {@code CookieRememberMeManager} with a default {@code rememberMe} cookie template. 
  16.      */  
  17.     public CookieRememberMeManager() {  
  18.         Cookie cookie = new SimpleCookie(DEFAULT_REMEMBER_ME_COOKIE_NAME);  
  19.         cookie.setHttpOnly(true);  
  20.         //One year should be long enough - most sites won't object to requiring a user to log in if they haven't visited  
  21.         //in a year:  
  22.         cookie.setMaxAge(Cookie.ONE_YEAR);  
  23.         this.cookie = cookie;  
  24.     }  
  25.   
  26.     public Cookie getCookie() {  
  27.         return cookie;  
  28.     }  
  29.   
  30.    
  31.     @SuppressWarnings({"UnusedDeclaration"})  
  32.     public void setCookie(Cookie cookie) {  
  33.         this.cookie = cookie;  
  34.     }  
  35.     protected void rememberSerializedIdentity(Subject subject, byte[] serialized) {  
  36.   
  37.         if (!WebUtils.isHttp(subject)) {  
  38.             if (log.isDebugEnabled()) {  
  39.                 String msg = "Subject argument is not an HTTP-aware instance.  This is required to obtain a servlet " +  
  40.                         "request and response in order to set the rememberMe cookie. Returning immediately and " +  
  41.                         "ignoring rememberMe operation.";  
  42.                 log.debug(msg);  
  43.             }  
  44.             return;  
  45.         }  
  46.   
  47.   
  48.         HttpServletRequest request = WebUtils.getHttpRequest(subject);  
  49.         HttpServletResponse response = WebUtils.getHttpResponse(subject);  
  50.   
  51.         //base 64 encode it and store as a cookie:  
  52.         String base64 = Base64.encodeToString(serialized);  
  53.   
  54.         Cookie template = getCookie(); //the class attribute is really a template for the outgoing cookies  
  55.         Cookie cookie = new SimpleCookie(template);  
  56.         cookie.setValue(base64);  
  57.         cookie.saveTo(request, response);  
  58.     }  
  59.   
  60.     private boolean isIdentityRemoved(WebSubjectContext subjectContext) {  
  61.         ServletRequest request = subjectContext.resolveServletRequest();  
  62.         if (request != null) {  
  63.             Boolean removed = (Boolean) request.getAttribute(ShiroHttpServletRequest.IDENTITY_REMOVED_KEY);  
  64.             return removed != null && removed;  
  65.         }  
  66.         return false;  
  67.     }  
  68.     protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) {  
  69.   
  70.         if (!WebUtils.isHttp(subjectContext)) {  
  71.             if (log.isDebugEnabled()) {  
  72.                 String msg = "SubjectContext argument is not an HTTP-aware instance.  This is required to obtain a " +  
  73.                         "servlet request and response in order to retrieve the rememberMe cookie. Returning " +  
  74.                         "immediately and ignoring rememberMe operation.";  
  75.                 log.debug(msg);  
  76.             }  
  77.             return null;  
  78.         }  
  79.   
  80.         WebSubjectContext wsc = (WebSubjectContext) subjectContext;  
  81.         if (isIdentityRemoved(wsc)) {  
  82.             return null;  
  83.         }  
  84.   
  85.         HttpServletRequest request = WebUtils.getHttpRequest(wsc);  
  86.         HttpServletResponse response = WebUtils.getHttpResponse(wsc);  
  87.   
  88.         String base64 = getCookie().readValue(request, response);  
  89.         // Browsers do not always remove cookies immediately (SHIRO-183)  
  90.         // ignore cookies that are scheduled for removal  
  91.         if (Cookie.DELETED_COOKIE_VALUE.equals(base64)) return null;  
  92.   
  93.         if (base64 != null) {  
  94.             base64 = ensurePadding(base64);  
  95.             if (log.isTraceEnabled()) {  
  96.                 log.trace("Acquired Base64 encoded identity [" + base64 + "]");  
  97.             }  
  98.             byte[] decoded = Base64.decode(base64);  
  99.             if (log.isTraceEnabled()) {  
  100.                 log.trace("Base64 decoded byte array length: " + (decoded != null ? decoded.length : 0) + " bytes.");  
  101.             }  
  102.             return decoded;  
  103.         } else {  
  104.             //no cookie set - new site visitor?  
  105.             return null;  
  106.         }  
  107.     }  
  108.     private String ensurePadding(String base64) {  
  109.         int length = base64.length();  
  110.         if (length % 4 != 0) {  
  111.             StringBuilder sb = new StringBuilder(base64);  
  112.             for (int i = 0; i < length % 4; ++i) {  
  113.                 sb.append('=');  
  114.             }  
  115.             base64 = sb.toString();  
  116.         }  
  117.         return base64;  
  118.     }  
  119.     protected void forgetIdentity(Subject subject) {  
  120.         if (WebUtils.isHttp(subject)) {  
  121.             HttpServletRequest request = WebUtils.getHttpRequest(subject);  
  122.             HttpServletResponse response = WebUtils.getHttpResponse(subject);  
  123.             forgetIdentity(request, response);  
  124.         }  
  125.     }  
  126.   
  127.     public void forgetIdentity(SubjectContext subjectContext) {  
  128.         if (WebUtils.isHttp(subjectContext)) {  
  129.             HttpServletRequest request = WebUtils.getHttpRequest(subjectContext);  
  130.             HttpServletResponse response = WebUtils.getHttpResponse(subjectContext);  
  131.             forgetIdentity(request, response);  
  132.         }  
  133.     }  
  134.     private void forgetIdentity(HttpServletRequest request, HttpServletResponse response) {  
  135.         getCookie().removeFrom(request, response);  
  136.     }  
  137. }  
从构造器中看到cookie.setMaxAge(Cookie.ONE_YEAR);设置cookie的有效时间为一年。也许,这对我们来说会有点长,我们可以更改这些设置。配置如下:

  1. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
  2.     <property name="realm" ref="shiroRealm" />  
  3.     <!-- <property name="sessionMode" value="native"/> -->  
  4.     <property name="cacheManager" ref="shiroCacheManager"/>  
  5.     <property name="sessionManager" ref="shiroSessionManager"/>  
  6.     <property name="rememberMeManager.cookie.name" value="rememberMe"/>  
  7.     <property name="rememberMeManager.cookie.domain" value=".xxx.com"/>  
  8.     <property name="rememberMeManager.cookie.path" value="/"/>  
  9.     <property name="rememberMeManager.cookie.maxAge" value="604800"/> <!-- 7天有效期 -->  
  10.     <!-- <property name="subjectDAO" ref="subjectDAO"/> -->  
  11. </bean>  
当然,我们也可以实现自己的rememberMeManager。


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多
    喜欢该文的人也喜欢 更多

    ×
    ×

    ¥.00

    微信或支付宝扫码支付:

    开通即同意《个图VIP服务协议》

    全部>>