日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringBoot 基于Shiro + Jwt + Redis的用户权限管理 (三) 鉴权

發布時間:2023/12/31 javascript 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot 基于Shiro + Jwt + Redis的用户权限管理 (三) 鉴权 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

項目Github地址:?https://github.com/baiye21/ShiroDemo

  • SpringBoot 基于Shiro + Jwt + Redis的用戶權限管理 (一) 簡介與配置

  • SpringBoot 基于Shiro + Jwt + Redis的用戶權限管理 (二) 認證?

  • SpringBoot 基于Shiro + Jwt + Redis的用戶權限管理 (三) 鑒權

  • 一,ShiroFilterFactoryBean配置

    FilterChainDefinitionMap排除需要權限才能訪問的URL,這樣才能進入自定義過濾器JwtFilter中進行后續驗證。

    比如,測試權限的URl為/role/OneRole.do,它不需要寫入FilterChainDefinitionMap中,如下圖

    ?二,自定義ShiroSessionManager

    認證成功后,將shiro生成的sessionid保存到了access_token中,所以攜帶token訪問時,需要從token將其取出。

    com.demo.config.ShiroConfig中

    /*** SecurityManager 是 Shiro 架構的核心,通過它來鏈接Realm和用戶(文檔中稱之為Subject.)*/@Beanpublic SecurityManager securityManager(@Qualifier("passwordRealm") PasswordRealm passwordRealm,@Qualifier("jwtRealm") JwtRealm jwtRealm,@Qualifier("demoRealm") DemoRealm demoRealm,@Qualifier("userModularRealmAuthenticator") UserModularRealmAuthenticator userModularRealmAuthenticator) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 設置realmsecurityManager.setAuthenticator(userModularRealmAuthenticator);List<Realm> realms = new ArrayList<>();// 添加多個realmrealms.add(passwordRealm);realms.add(jwtRealm);realms.add(demoRealm);/** 關閉shiro自帶的session,詳情見文檔* http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29*/DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();// 不需要將 Shiro Session 中的東西存到任何地方(包括 Http Session 中)defaultSessionStorageEvaluator.setSessionStorageEnabled(false);subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);securityManager.setSubjectDAO(subjectDAO);// 自定義sessionManagersecurityManager.setSessionManager(shiroSessionManager());// securityManager設置自定義認證規則securityManager.setRealms(realms);return securityManager;}@Beanpublic ShiroSessionManager shiroSessionManager() {ShiroSessionManager shiroSessionManager = new ShiroSessionManager();// TODO redis 配置session持久化shiroSessionManager.setSessionDAO(new EnterpriseCacheSessionDAO());return shiroSessionManager;}

    com.demo.shiro.ShiroSessionManager

    @Slf4j public class ShiroSessionManager extends DefaultWebSessionManager {public ShiroSessionManager() {super();}private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";@Overrideprotected Serializable getSessionId(ServletRequest request, ServletResponse response) {// 如果請求頭中有 AuthorizationString token = WebUtils.toHttp(request).getHeader(Const.TOKEN_HEADER_NAME);if (!StringUtils.isEmpty(token)) {if (JwtUtil.verify(token, Const.TOKEN_SECRET)) {String id = JwtUtil.getClaim(token, Const.JSESSIONID);log.debug("ShiroSessionManager從http header 取出token中的JSESSIONID:{}", id);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,REFERENCED_SESSION_ID_SOURCE);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);return id;}return super.getSessionId(request, response);} else {// 否則按默認規則從cookie取sessionIdreturn super.getSessionId(request, response);}}}

    三,JwtFilter執行executeLogin();

    /*** 執行登錄認證* * @param request* @param response* @param mappedValue* @return 是否成功*/@Override// 這個方法判斷 嘗試進行登錄的操作,如果token存在,那么進行提交登錄,如果不存在說明可能是正在進行登錄或者做其它的事情 直接放過即可protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {try {executeLogin(request, response);return true;} catch (Exception e) {// return false;// throw new AuthenticationException("Token失效請重新登錄");// 認證出現異常,傳遞錯誤信息msgString msg = e.getMessage();// 獲取應用異常(該Cause是導致拋出此throwable(異常)的throwable(異常))Throwable throwable = e.getCause();if (throwable != null && throwable instanceof SignatureVerificationException) {// 該異常為JWT的AccessToken認證失敗(Token或者密鑰不正確)msg = "token或者密鑰不正確(" + throwable.getMessage() + ")";} else if (throwable != null && throwable instanceof TokenExpiredException) {// 該異常為JWT的AccessToken已過期(TokenExpiredException),// 判斷RefreshToken未過期就進行AccessToken刷新if (this.refreshToken(request, response)) {return true;} else {msg = "token已過期(" + throwable.getMessage() + ")";}} else {// 應用異常不為空if (throwable != null) {// 獲取應用異常msgmsg = throwable.getMessage();}}/*** 錯誤兩種處理方式* 1. 將非法請求轉發到/401的Controller處理,拋出自定義無權訪問異常被全局捕捉再返回Response信息* 2. 無需轉發,直接返回Response信息 一般使用第二種(更方便)*/// 直接返回Response信息this.response401(request, response, msg);return false;}}

    四,UserModularRealmAuthenticator加載DemoRealm

    login的時候創建的是CustomizedToken,之后的請求Header攜帶的都是JwtToken,因此區分加載哪一種類型的Realm。

    if(authenticationToken instanceof JwtToken) {log.debug("驗證的Token類型是:{}", "JwtToken");typeRealms.clear();// 獲取header部的token進行強制類型轉換JwtToken jwtToken = (JwtToken) authenticationToken;for (Realm realm : realms) {if (realm.getName().contains("Demo")) {typeRealms.add(realm);}}return doSingleRealmAuthentication(typeRealms.iterator().next(), jwtToken);}

    五,DemoRealm進行鑒權(doGetAuthorizationInfo)

    /*** 功能: 獲取用戶權限信息,包括角色以及權限。只有當觸發檢測用戶權限時才會調用此方法,例如checkRole,checkPermission** @param principals* @return AuthorizationInfo 權限信息*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {log.info("demoRealm doGetAuthorizationInfo 用戶賦權 ");String userid = null;if (principals != null) {// 此處的principals為 UserMasterObject PrimaryPrincipal = principals.getPrimaryPrincipal();if (PrimaryPrincipal instanceof UserMaster) {UserMaster userMaster = (UserMaster) PrimaryPrincipal;userid = userMaster.getUserId();} else {// 此處的principals為tokenuserid = JwtUtil.getClaim(principals.toString(), Const.TOKEN_CLAIM_USERID);}}SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();// 獲取用戶角色與權限信息UserAccessInfo userAccessInfo = iUserService.getUserAccessInfo(userid);/*** 設置用戶擁有的角色集合,<br>* * accountType = 1 管理員 admin <br>* accountType = 2 領導 Leader <br>* accountType = 3 普通用戶 user <br>* */info.setRoles(userAccessInfo.getRoleSet());// 設置用戶擁有的權限集合info.addStringPermissions(userAccessInfo.getPermissionSet());return info;}

    六,getUserAccessInfo

    com.demo.service.impl.UserServiceImpl的getUserAccessInfo方法

    /*** 獲取用戶角色set與權限set** @param userId* @return*/public UserAccessInfo getUserAccessInfo(String userId) {UserAccessInfo userAccessInfo = new UserAccessInfo();// 用戶角色setSet<String> roleSet = new HashSet<String>();// 用戶權限setSet<String> permissionSet = new HashSet<String>();UserMaster userMaster = userMasterMapper.selectByUserId(userId);// 賬號類型String accountType = userMaster.getAccountType();// 管理員 accountType = 1if (Const.ADMIN_USER_CODE.equals(accountType)) {// adminroleSet.add(Const.ADMIN_USER);// Leader accountType = 2} else if (Const.LEADWER_USER_CODE.equals(accountType)) {// LeaderroleSet.add(Const.LEADER_USER);// 普通用戶 accountType = 3} else if (Const.NORMAL_USER_CODE.equals(accountType)) {// userroleSet.add(Const.NORMAL_USER);}// 用戶權限類型String permissionType = userMaster.getPermissionType();if (Const.LEVEL_001_CODE.equals(permissionType)) {permissionSet.add(Const.LEVEL_001);} else if (Const.LEVEL_002_CODE.equals(permissionType)) {permissionSet.add(Const.LEVEL_002);} else if (Const.LEVEL_003_CODE.equals(permissionType)) {permissionSet.add(Const.LEVEL_003);} else if (Const.LEVEL_004_CODE.equals(permissionType)) {permissionSet.add(Const.LEVEL_004);} else if (Const.LEVEL_005_CODE.equals(permissionType)) {permissionSet.add(Const.LEVEL_005);}userAccessInfo.setRoleSet(roleSet);userAccessInfo.setPermissionSet(permissionSet);return userAccessInfo;}

    七,簡單測試

    測試用的TestRoleController ,主要測試以下權限:

    ps:由于之前測試用戶表設計一個用戶只對應一種角色一種權限,所以第四個測試只有失敗的情況

  • ? @RequiresRoles(Const.ADMIN_USER)? 角色-- 只有管理員用戶才能訪問
  • ? @RequiresRoles(value = { Const.ADMIN_USER, Const.LEADER_USER }, logical = Logical.OR)??角色-- 管理員用戶和Leader用戶都能訪問
  • ??@RequiresPermissions(Const.LEVEL_004)? 權限-- 001 能訪問
  • ? @RequiresPermissions(value = {Const.LEVEL_004,Const.LEVEL_005}, logical = Logical.AND)? 權限-- 004 且 005?才能訪問
  • /** Author : baiye <baiye_21@163.com>Time : 2021/06/30Function: */ @RestController @RequestMapping("/role/") public class TestRoleController {/*** 只有管理員用戶才能訪問** @return* @throws Exception*/@RequiresRoles(Const.ADMIN_USER)@RequestMapping(value = "/OneRole.do", method = RequestMethod.POST)@ResponseBodypublic ServerResponse<String> OneRole() throws Exception {return ServerResponse.createBySuccessMessage("One Role");}/*** 管理員用戶和Leader用戶能訪問** @return* @throws Exception*/@RequiresRoles(value = { Const.ADMIN_USER, Const.LEADER_USER }, logical = Logical.OR)@RequestMapping(value = "/TwoRole.do", method = RequestMethod.POST)@ResponseBodypublic ServerResponse<String> TwoRole() throws Exception {return ServerResponse.createBySuccessMessage("Two Role");}/*** Permission 001 能訪問** @return* @throws Exception*/@RequiresPermissions(Const.LEVEL_004)@RequestMapping(value = "/OnePermission.do", method = RequestMethod.POST)@ResponseBodypublic ServerResponse<String> OnePermission() throws Exception {return ServerResponse.createBySuccessMessage("One Permission");}/*** Permission 004 且 005 能訪問** @return* @throws Exception*/@RequiresPermissions(value = {Const.LEVEL_004,Const.LEVEL_005}, logical = Logical.AND)@RequestMapping(value = "/TwoPermission.do", method = RequestMethod.POST)@ResponseBodypublic ServerResponse<String> TwoPermission() throws Exception {return ServerResponse.createBySuccessMessage("Two Permission");} }

    準備的測試用戶

    使用demo002用戶登錄

    localhost:9999/role/OneRole.do

    localhost:9999/role/TwoRole.do

    ?localhost:9999/role/OnePermission.do

    ?localhost:9999/role/TwoPermission.do

    總結

    以上是生活随笔為你收集整理的SpringBoot 基于Shiro + Jwt + Redis的用户权限管理 (三) 鉴权的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。