shiro基礎整合jwt,可在項目中直接使用呦

      網友投稿 687 2022-05-30

      前幾天給大家講解了一下shiro,后臺一些小伙伴跑來給我留言說:“一般不都是shiro結合jwt做身份和權限驗證嗎?能不能再講解一下jwt的用法呢?“今天阿Q就給大家講一下shiro整合jwt做權限校驗吧。

      首先呢我還是要說一下jwt的概念:JWT全稱Json web token , 是為了在網絡應用環境間傳遞聲明而執行的一種基于JSON的開放標準。該token被設計為緊湊且安全的,特別適用于分布式站點的單點登錄(SSO)場景。JWT的聲明一般被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便于從資源服務器獲取資源,也可以增加一些額外的其它業務邏輯所必須的聲明信息,該token也可直接被用于認證,也可被加密。通俗點說呢,就是之前的session為了區分是哪個用戶發來的請求,需要在服務端存儲用戶信息,需要消耗服務器資源。并且隨著用戶量的增大,勢必會擴展服務器,采用分布式系統,這樣的話session就可能就不太合適了,而我們今天說的jwt呢就很好地解決了單點登錄問題,很容易的解決session共享的問題。

      話不多說,直接上整合教程(本期是在上期shiro的基礎上進行的改造):

      一、在pom文件中引入jwt的依賴包

      com.auth0 java-jwt 3.7.0

      二、寫一個工具類用于生成簽名和驗證簽名

      public class JwtUtil { //JWT-account public static final String ACCOUNT = "username"; //JWT-currentTimeMillis public final static String CURRENT_TIME_MILLIS = "currentTimeMillis"; //有效期時間2小時 public static final long EXPIRE_TIME = 2 * 60 * 60 * 1000L; //秘鑰 public static final String SECRET_KEY = "shirokey"; /** * 生成簽名返回token * * @param account * @param currentTimeMillis * @return */ public static String sign(String account, String currentTimeMillis) { // 帳號加JWT私鑰加密 String secret = account + SECRET_KEY; // 此處過期時間,單位:毫秒,在當前時間到后邊的20分鐘內都是有效的 Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); //采用HMAC256加密 Algorithm algorithm = Algorithm.HMAC256(secret); return JWT.create() .withClaim(ACCOUNT, account) .withClaim(CURRENT_TIME_MILLIS, currentTimeMillis) .withExpiresAt(date) //創建一個新的JWT,并使用給定的算法進行標記 .sign(algorithm); } /** * 校驗token是否正確 * * @param token * @return */ public static boolean verify(String token) { String secret = getClaim(token, ACCOUNT) + SECRET_KEY; Algorithm algorithm = Algorithm.HMAC256(secret); JWTVerifier verifier = JWT.require(algorithm) .build(); verifier.verify(token); return true; } /** * 獲得Token中的信息無需secret解密也能獲得 * * @param token * @param claim * @return */ public static String getClaim(String token, String claim) { try { DecodedJWT jwt = JWT.decode(token); return jwt.getClaim(claim).asString(); } catch (JWTDecodeException e) { return null; } } }

      三、封裝自己的token,用于后邊校驗token類型

      public class JwtToken implements AuthenticationToken { private final String token; public JwtToken(String token){ this.token = token; } @Override public Object getPrincipal() { return token; } @Override public Object getCredentials() { return token; }

      四、我們需要在登陸時創建token

      在shiro基礎上整合jwt,可在項目中直接使用呦

      //service中的登錄處理 @Override public UserTokenDTO login(UserTokenDTO userInfo) { // 從數據庫獲取對應用戶名密碼的用戶 SysUserInfo uInfo = userInfoMapper.getUserByLogin(userInfo.getName()); if (null == uInfo) { //用戶信息不存在 throw new BusinessException(CommonResultStatus.USERNAME_ERROR); } else if (!userInfo.getPassword().equals(uInfo.getPassword())) { //密碼錯誤 throw new BusinessException(CommonResultStatus.PASSWORD_ERROR); } //生成jwtToken userInfo.setToken(JwtUtil.sign(userInfo.getName(),String.valueOf(System.currentTimeMillis()))); return userInfo; }

      五、在其他需要登錄后才能訪問的請求中解析token,所以我們要自定義過濾器

      public class JwtFilter extends AccessControlFilter { //設置請求頭中需要傳遞的字段名 protected static final String AUTHORIZATION_HEADER = "Access-Token"; /** * 表示是否允許訪問,mappedValue就是[urls]配置中-參數部分, * 如果允許訪問返回true,否則false * @author cheetah * @date 2020/11/24 * @param request: * @param response: * @param mappedValue: * @return: boolean */ @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { return false; } /** * 表示當訪問拒絕時是否已經處理了, * 如果返回true表示需要繼續處理, * 如果返回false表示該-實例已經處理了,將直接返回即可 * @author cheetah * @date 2020/11/24 * @param request: * @param response: * @return: boolean */ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { HttpServletRequest req = (HttpServletRequest) request; // 解決跨域問題 if(HttpMethod.OPTIONS.toString().matches(req.getMethod())) { return true; } if (isLoginAttempt(request, response)) { //生成jwt token JwtToken token = new JwtToken(req.getHeader(AUTHORIZATION_HEADER)); //委托給Realm進行驗證 try { //調用登陸會走Realm中的身份驗證方法 getSubject(request, response).login(token); return true; } catch (Exception e) { } }else{ throw new BusinessException(CommonResultStatus.LOGIN_ERROR); } return false; } /** * 判斷是否有頭部參數 * @author cheetah * @date 2020/11/24 * @param request: * @param response: * @return: boolean */ protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) { HttpServletRequest req = (HttpServletRequest) request; String authorization = req.getHeader(AUTHORIZATION_HEADER); return authorization != null; } }

      六、當濾器中調用subject.login(token)方法時,會走自定義Realm中的doGetAuthenticationInfo(AuthenticationToken token)方法來驗證身份

      @Slf4j public class JwtRealm extends AuthorizingRealm { @Autowired private UserInfoService userService; //驗證是不是自己的token類型 @Override public boolean supports(AuthenticationToken token) { return token instanceof JwtToken; } /** * 身份驗證 * @author cheetah * @date 2020/11/25 * @param token: * @return: org.apache.shiro.authc.AuthenticationInfo */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String credentials = (String) token.getCredentials(); String username = null; try { //jwt驗證token boolean verify = JwtUtil.verify(credentials); if (!verify) { throw new AuthenticationException("Token校驗不正確"); } username = JwtUtil.getClaim(credentials, JwtUtil.ACCOUNT); } catch (Exception e) { throw new BusinessException(CommonResultStatus.TOKEN_CHECK_ERROR,e.getMessage()); } //交給AuthenticatingRealm使用CredentialsMatcher進行密碼匹配,不設置則使用默認的SimpleCredentialsMatcher SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( username, //用戶名 credentials, //憑證 getName() //realm name ); return authenticationInfo; } /** * 權限校驗(次數不做過多講解) * @author cheetah * @date 2020/11/25 * @param principals: * @return: org.apache.shiro.authz.AuthorizationInfo */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // String username = principals.toString(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); //角色權限暫時不加 // authorizationInfo.setRoles(userService.getRoles(username)); // authorizationInfo.setStringPermissions(userService.queryPermissions(username)); return authorizationInfo; } }

      七、接下來我們需要修改ShiroConfig文件,將自定義的Filter和Realm交由SecurityManager進行管理

      /** 此類較長,只展示部分重要代碼,其余代碼可在公眾號“阿Q說”中回復"jwt"獲取源碼 **/ @Configuration @Slf4j public class ShiroConfig { /** * 創建ShiroFilterFactoryBean * @author cheetah * @date 2020/11/21 * @return: org.apache.shiro.spring.web.ShiroFilterFactoryBean */ @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必須設置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //設置shiro內置過濾器 Map filters = new LinkedHashMap<>(); //添加自定義過濾器:只對需要登陸的接口進行過濾 filters.put("authc", new JwtFilter()); //添加自定義過濾器:對權限進行驗證 // filters.put("roles", new CustomRolesOrAuthorizationFilter()); shiroFilterFactoryBean.setFilters(filters); // setLoginUrl 如果不設置值,默認會自動尋找Web工程根目錄下的"/login.jsp"頁面 或 "/login" 映射 shiroFilterFactoryBean.setLoginUrl("/adminLogin/login"); // 設置無權限時跳轉的 url; shiroFilterFactoryBean.setUnauthorizedUrl("/notAuth"); // 設置- Map filterChainDefinitionMap = new LinkedHashMap<>(); //游客,開發權限 filterChainDefinitionMap.put("/guest/**", "anon"); //用戶,需要角色權限 “user” filterChainDefinitionMap.put("/user/**", "roles[user]"); // filterChainDefinitionMap.put("/productInfo/**", "roles[user]"); //管理員,需要角色權限 “admin” filterChainDefinitionMap.put("/admin/**", "roles[admin]"); //開放登陸接口 filterChainDefinitionMap.put("/adminLogin/login", "anon"); //其余接口一律攔截 //主要這行代碼必須放在所有權限設置的最后,不然會導致所有 url 都被攔截 filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); log.info("-------Shiro-工廠類注入成功-----------"); return shiroFilterFactoryBean; } /** * 注入安全管理器 * @author cheetah * @date 2020/11/21 * @return: java.lang.SecurityManager */ @Bean public DefaultWebSecurityManager securityManager(JwtRealm jwtRealm, SubjectFactory subjectFactory, SessionManager sessionManager, CacheManager cacheManager) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(jwtRealm); //關閉shiro自帶的session DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); defaultSessionStorageEvaluator.setSessionStorageEnabled(false); subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); securityManager.setSubjectDAO(subjectDAO); securityManager.setSubjectFactory(subjectFactory); securityManager.setSessionManager(sessionManager); securityManager.setCacheManager(cacheManager); return securityManager; } /** * jwt身份認證和權限校驗 * @author cheetah * @date 2020/11/24 * @return: com.cheetah.shiroandjwt.jwt.JwtRealm */ @Bean public JwtRealm jwtRealm() { JwtRealm jwtRealm = new JwtRealm(); jwtRealm.setAuthenticationCachingEnabled(true); jwtRealm.setAuthorizationCachingEnabled(true); return jwtRealm; } }

      重點:將自定義的Realm交由SecurityManage管理,關閉shiro自帶的session

      接下來我們啟動成程序驗證一下:當我們未登錄時,請求失敗,需要先登錄

      登錄成功獲取token信息

      當帶著頭部信息"Access-Token"訪問時就可以獲取信息了。

      想了解更多學習知識,請關注“阿Q說代碼”,回復“jwt”可獲取本期源碼!你也可以后臺留言說出你的疑惑,阿Q將會在后期的文章中為你解答。每天學習一點點,每天進步一點點。

      JSON

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:華為全球最大數據中心落地貴州,這些硬核技術有利支撐“東數西算”
      下一篇:(更新時間)2021年5月28日 商城高并發秒殺系統(.NET Core版) 06-注冊中心組件的封裝
      相關文章
      中文字幕亚洲乱码熟女一区二区| 亚洲综合免费视频| 亚洲国产成人精品女人久久久 | 亚洲自偷精品视频自拍| 亚洲热线99精品视频| 中文字幕不卡亚洲| 亚洲欭美日韩颜射在线二| 久久亚洲国产精品五月天婷| 亚洲区精品久久一区二区三区| 亚洲自偷自拍另类图片二区| 亚洲日韩国产精品无码av| 亚洲日韩国产精品无码av| 亚洲福利电影一区二区?| 亚洲福利电影一区二区?| 亚洲成A∨人片在线观看无码| 亚洲视频一区二区三区| 亚洲韩国精品无码一区二区三区 | 亚洲精品成人久久久| 亚洲第一永久AV网站久久精品男人的天堂AV| 国产精品亚洲一区二区在线观看| 亚洲国产精华液2020| 国产精品亚洲综合天堂夜夜| 噜噜噜亚洲色成人网站| 国产亚洲?V无码?V男人的天堂| 亚洲乱码精品久久久久..| 亚洲AV无码欧洲AV无码网站| 亚洲永久永久永久永久永久精品| 久久精品国产亚洲AV无码偷窥 | 精品韩国亚洲av无码不卡区| 亚洲精品老司机在线观看| 中文字幕亚洲一区二区va在线| 亚洲理论电影在线观看| 亚洲AV无码乱码国产麻豆穿越| 亚洲黄网站wwwwww| 亚洲一级高清在线中文字幕| 国产亚洲精品VA片在线播放| 亚洲av纯肉无码精品动漫| 亚洲精品麻豆av| 亚洲成AV人片在线观看WWW| 亚洲黄色网址在线观看| 亚洲日韩一区精品射精|