shiro token 分析


1.ShiroConfig.java 定義匿名用戶可以訪問的資源

  filterMap.put("/webjars/**", "anon");
        filterMap.put("/druid/**", "anon");
        filterMap.put("/api/**", "anon");
        filterMap.put("/sys/login", "anon");
        filterMap.put("/**/*.css", "anon");
        filterMap.put("/**/*.js", "anon");
        filterMap.put("/**/*.html", "anon");
        filterMap.put("/fonts/**", "anon");
        filterMap.put("/plugins/**", "anon");
        filterMap.put("/swagger/**", "anon");
        filterMap.put("/favicon.ico", "anon");
        filterMap.put("/", "anon");
        filterMap.put("/**", "oauth2");           --除了anon,攔截其他所有請求

2.OAuth2Filter.java 基於shiro的全局過濾器

繼承AuthenticatingFilter 實現createToken、isAccessAllowed、onAccessDenied、onLoginFailure等抽象方法

import com.google.gson.Gson;
import io.renren.common.utils.R;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class OAuth2Filter extends AuthenticatingFilter {

    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
        //獲取請求token
        String token = getRequestToken((HttpServletRequest) request);

        if(StringUtils.isBlank(token)){
            return null;
        }
        return new OAuth2Token(token);
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        //獲取請求token,如果token不存在,直接返回401
        String token = getRequestToken((HttpServletRequest) request);
        if(StringUtils.isBlank(token)){
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            String json = new Gson().toJson(R.error(HttpStatus.SC_UNAUTHORIZED, "invalid token"));
            httpResponse.getWriter().print(json);
            return false;
        }
        System.out.println("onAccessDenied-----------------------onAccessDenied");
        return executeLogin(request, response);
    }

    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setContentType("application/json;charset=utf-8");
        try {
            //處理登錄失敗的異常
            Throwable throwable = e.getCause() == null ? e : e.getCause();
            R r = R.error(HttpStatus.SC_UNAUTHORIZED, throwable.getMessage());

            String json = new Gson().toJson(r);
            httpResponse.getWriter().print(json);
        } catch (IOException e1) {

        }
        return false;
    }

    /**
     * 獲取請求的token
     */
    private String getRequestToken(HttpServletRequest httpRequest){
        //從header中獲取token
        String token = httpRequest.getHeader("token");

        //如果header中不存在token,則從參數中獲取token
        if(StringUtils.isBlank(token)){
            token = httpRequest.getParameter("token");
        }
        return token;
    }
}

如果成功獲得token 則繼續調用父類中executeLogin方法,此方法實現如下

protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        AuthenticationToken token = createToken(request, response);
        if (token == null) {
            String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
                    "must be created in order to execute a login attempt.";
            throw new IllegalStateException(msg);
        }
        try {
       // 創建主題,然后繼續調用Realm中的登入認證方法doGetAuthenticationInfo Subject subject
= getSubject(request, response); subject.login(token); return onLoginSuccess(token, subject, request, response); } catch (AuthenticationException e) { return onLoginFailure(token, e, request, response); } }

調用子類中的createToken方法獲得token對象,將token對象賦值給shiro subject 對象,從而在后面的認證方法中獲得token

3.將OAuth2Realm 注冊到Shiro Seurity中,ShiroConfig.securityManager

 1 package io.renren.config;
 2 
 3 import io.renren.modules.sys.oauth2.OAuth2Filter;
 4 import io.renren.modules.sys.oauth2.OAuth2Realm;
 5 import org.apache.shiro.mgt.SecurityManager;
 6 import org.apache.shiro.session.mgt.SessionManager;
 7 import org.apache.shiro.spring.LifecycleBeanPostProcessor;
 8 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
 9 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
10 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
11 import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
12 import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
13 import org.springframework.context.annotation.Bean;
14 import org.springframework.context.annotation.Configuration;
15 
16 import javax.servlet.Filter;
17 import java.util.HashMap;
18 import java.util.LinkedHashMap;
19 import java.util.Map;
20 
21 
22 @Configuration
23 public class ShiroConfig {
24 
25     @Bean("sessionManager")
26     public SessionManager sessionManager(){
27         DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
28         sessionManager.setSessionValidationSchedulerEnabled(true);
29         sessionManager.setSessionIdCookieEnabled(false);
30         System.out.println("獲得sessionManager:" + sessionManager);
31         return sessionManager;
32     }
33 
34     @Bean("securityManager")
35     public SecurityManager securityManager(OAuth2Realm oAuth2Realm, SessionManager sessionManager) {
36         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
37         securityManager.setRealm(oAuth2Realm);
38         securityManager.setSessionManager(sessionManager);
39         System.out.println("獲得SecurityManager:" + securityManager);
40         return securityManager;
41     }
42 
43     @Bean("shiroFilter")
44     public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
45         ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
46         shiroFilter.setSecurityManager(securityManager);
47 
48         //oauth過濾
49         Map<String, Filter> filters = new HashMap<>();
50         filters.put("oauth2", new OAuth2Filter());
51         shiroFilter.setFilters(filters);
52 
53         Map<String, String> filterMap = new LinkedHashMap<>();
54         filterMap.put("/webjars/**", "anon");
55         filterMap.put("/druid/**", "anon");
56         filterMap.put("/api/**", "anon");
57         filterMap.put("/sys/login", "anon");
58         filterMap.put("/**/*.css", "anon");
59         filterMap.put("/**/*.js", "anon");
60         filterMap.put("/**/*.html", "anon");
61         filterMap.put("/fonts/**", "anon");
62         filterMap.put("/plugins/**", "anon");
63         filterMap.put("/swagger/**", "anon");
64         filterMap.put("/favicon.ico", "anon");
65         filterMap.put("/", "anon");
66         filterMap.put("/**", "oauth2");
67         shiroFilter.setFilterChainDefinitionMap(filterMap);
68         System.out.println("獲得shiroFilter:" + shiroFilter);
69         return shiroFilter;
70     }
71 
72     @Bean("lifecycleBeanPostProcessor")
73     public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
74         return new LifecycleBeanPostProcessor();
75     }
76 
77     @Bean
78     public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
79         DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
80         proxyCreator.setProxyTargetClass(true);
81         return proxyCreator;
82     }
83 
84     @Bean
85     public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
86         AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
87         advisor.setSecurityManager(securityManager);
88         return advisor;
89     }
90 
91 }
View Code

4.每次請求都會先調用OAuth2Realm中的doGetAuthenticationInfo方法驗證token的合法性,然后再調用doGetAuthorizationInfo驗證權限

5.通過common.js判斷當前客戶端是否緩存了token,如果沒有則跳轉至login.html

//登錄token
var token = localStorage.getItem("token");
if(token == 'null'){
    parent.location.href = baseURL + 'login.html';
}

6.登入頁面輸入用戶名、密碼之后 緩存token,並跳轉至index.html

login: function () {
            var data = "username="+vm.username+"&password="+vm.password;
            $.ajax({
                type: "POST",
                url: baseURL + "sys/login",
                data: data,
                dataType: "json",
                success: function(r){
                    if(r.code == 0){//登錄成功
                        localStorage.setItem("token", r.token);
                        parent.location.href ='index.html';
                    }else{
                        vm.error = true;
                        vm.errorMsg = r.msg;
                    }
                }
            });
        }
View Code

7.LoginController.java

/**
     * 登錄
     */
    @RequestMapping(value = "/sys/login", method = RequestMethod.POST)
    public Map<String, Object> login(String username, String password)throws IOException {
        //用戶信息
        SysUserEntity user = sysUserService.queryByUserName(username);

        //賬號不存在、密碼錯誤
        if(user == null || !user.getPassword().equals(new Sha256Hash(password, user.getSalt()).toHex())) {
            return R.error("賬號或密碼不正確");
        }

        //賬號鎖定
        if(user.getStatus() == 0){
            return R.error("賬號已被鎖定,請聯系管理員");
        }

        //生成token,並保存到數據庫
        R r = sysUserTokenService.createToken(user.getUserId());
        return r;
    }

 

8. 數據庫token表結構,該表結構改成redis即可實現sso單點登入功能

  Field Type Comment
user_id bigint(20) NOT NULL  
  token varchar(100) NOT NULL token
  expire_time datetime NULL 過期時間
  update_time datetime NULL 更新時間


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM