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

歡迎訪問 生活随笔!

生活随笔

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

javascript

学习Spring Boot:(十三)配置 Shiro 权限认证

發布時間:2025/3/12 javascript 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 学习Spring Boot:(十三)配置 Shiro 权限认证 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

經過前面學習 Apache Shiro ,現在結合 Spring Boot 使用在項目里,進行相關配置。

正文

添加依賴

在 pom.xml 文件中添加 shiro-spring 的依賴:

<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro.version}</version></dependency>

RBAC

RBAC 1 是基于角色的訪問控制,權限與角色關聯,給用戶配置相關角色,來獲取權限信息。

Shiro 配置

新建一個新的 Shiro 配置類 ShiroConfig:

package com.wuwii.common.config;import com.wuwii.module.sys.autho2.OAuth2Realm; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.mgt.SessionManager; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;import java.util.LinkedHashMap; import java.util.Map;/*** Apache Shiro 核心通過 Filter 來實現,就好像SpringMvc 通過DispachServlet 來主控制一樣。* 既然是使用 Filter 一般也就能猜到,是通過URL規則來進行過濾和權限校驗,* 所以我們需要定義一系列關于URL的規則和訪問權限。** @author KronChan* @version 1.0* @since <pre>2018/2/9 10:35</pre>*/ @Configuration public class ShiroConfig {@Beanpublic SessionManager sessionManager() {DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();sessionManager.setSessionValidationSchedulerEnabled(true);sessionManager.setSessionIdCookieEnabled(true);return sessionManager;}/*** 注冊安全管理,必須設置 SecurityManager** @param oAuth2Realm 認證* @param sessionManager 緩存* @return*/@Beanpublic SecurityManager securityManager(OAuth2Realm oAuth2Realm, SessionManager sessionManager) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 可以添加多個認證,執行順序是有影響的securityManager.setRealm(oAuth2Realm);securityManager.setSessionManager(sessionManager);return securityManager;}@Beanpublic ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();shiroFilter.setSecurityManager(securityManager);//自定義一個oauth2攔截器,不設置就是使用默認的攔截器/*Map<String, Filter> filters = new HashMap<>();filters.put("oauth2", new OAuth2Filter());shiroFilter.setFilters(filters);*///攔截器//<!-- 過濾鏈定義,從上向下順序執行,一般將 /**放在最為下邊 -->//<!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問-->Map<String, String> filterMap = new LinkedHashMap<>();//配置退出過濾器,其中的具體的退出代碼Shiro已經替我們實現了filterMap.put("/sys/logout", "logout");// 驗證碼filterMap.put("/sys/captcha.jpg", "anon");// 設置系統模塊下訪問需要權限filterMap.put("/sys/login", "anon");// 自定義的攔截//filterMap.put("/sys/**", "oauth2");filterMap.put("/sys/**", "authc");// 登陸的 urlshiroFilter.setLoginUrl("/sys/login");// 登陸成功跳轉的 urlshiroFilter.setSuccessUrl("/");// 未授權的 url// shiroFilter.setUnauthorizedUrl("/login.html");//未授權界面;shiroFilter.setUnauthorizedUrl("/403");shiroFilter.setFilterChainDefinitionMap(filterMap);return shiroFilter;}/*** Shiro生命周期處理器* @return*/@Beanpublic LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/*** 開啟Shiro的注解,* (如@RequiresRoles,@RequiresPermissions),需借助SpringAOP掃描使用Shiro注解的類,* 并在必要時進行安全邏輯驗證 * 配置以下兩個bean(DefaultAdvisorAutoProxyCreator(可選)和AuthorizationAttributeSourceAdvisor)即可實現此功能** @return*/@Beanpublic DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();proxyCreator.setProxyTargetClass(true);return proxyCreator;}/*** 開啟 shiro aop注解支持.** @param securityManager* @return*/@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;}/*** 憑證匹配器* (由于我們的密碼校驗交給Shiro的SimpleAuthenticationInfo進行處理了* 所以我們需要修改下doGetAuthenticationInfo中的代碼;* )* <b>需要在身份認證中添加 realm.setCredentialsMatcher(hashedCredentialsMatcher())</b>* @return*//*@Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher() {HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:這里使用MD5算法;hashedCredentialsMatcher.setHashIterations(2);//散列的次數,比如散列兩次,相當于 md5(md5(""));return hashedCredentialsMatcher;}*/ }

Filter Chain定義說明:

  • 一個URL可以配置多個Filter,使用逗號分隔
  • 當設置多個過濾器時,全部驗證通過,才視為通過
  • 部分過濾器可指定參數,如perms,roles
  • Shiro內置的FilterChain:

    Filter NameClass
    anonorg.apache.shiro.web.filter.authc.AnonymousFilter
    authcorg.apache.shiro.web.filter.authc.FormAuthenticationFilter
    authcBasicorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
    permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
    portorg.apache.shiro.web.filter.authz.PortFilter
    restorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
    rolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter
    sslorg.apache.shiro.web.filter.authz.SslFilter
    userorg.apache.shiro.web.filter.authc.UserFilter

    * anon:所有url都都可以匿名訪問
    * authc: 需要認證才能進行訪問
    * user:配置記住我或認證通過可以訪問

    自定義的攔截器(可選)

    如果需要按照自己的需要定義一個 oauth2 的攔截器,則需要 繼承 AuthenticatingFilter 實現幾個方法即可。

    /*** oauth2過濾器*/ public class OAuth2Filter extends AuthenticatingFilter {/*** logger*/private static final Logger LOGGER = LoggerFactory.getLogger(OAuth2Filter.class);@Overrideprotected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {//獲取請求tokenString token = getRequestToken((HttpServletRequest) request);if (StringUtils.isBlank(token)) {return null;}return new OAuth2Token(token);}/*** shiro權限攔截核心方法 返回true允許訪問resource,* @param request* @param response* @param mappedValue* @return*/@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {return false;}/*** 當訪問拒絕時是否已經處理了;* 如果返回true表示需要繼續處理;* 如果返回false表示該攔截器實例已經處理完成了,將直接返回即可。* @param request* @param response* @return* @throws Exception*/@Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {//獲取請求token,如果token不存在,直接返回401String token = getRequestToken((HttpServletRequest) request);if (StringUtils.isBlank(token)) {HttpServletResponse httpResponse = (HttpServletResponse) response;((HttpServletResponse) response).setStatus(401);response.getWriter().print("沒有權限,請聯系管理員授權");return false;}return executeLogin(request, response);}/*** 鑒定失敗,返回錯誤信息* @param token* @param e* @param request* @param response* @return*/@Overrideprotected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {try {((HttpServletResponse) response).setStatus(401);response.getWriter().print("沒有權限,請聯系管理員授權");} catch (IOException e1) {LOGGER.error(e1.getMessage(), e1);}return false;}/*** 獲取請求的token*/private String getRequestToken(HttpServletRequest httpRequest) {//從header中獲取tokenString token = httpRequest.getHeader("token");//如果header中不存在token,則從參數中獲取tokenif (StringUtils.isBlank(token)) {return httpRequest.getParameter("token");}// 還可以實現從 cookie 獲取Cookie[] cookies = httpRequest.getCookies();if(null==cookies||cookies.length==0){return null;}for (Cookie cookie : cookies) {if (cookie.getName().equals("token")) {token = cookie.getValue();continue;}}return token;} }

    具體實現可以參考我的上篇文章 《Apache Shiro的攔截器和認證》

    認證實現

    Shiro的認證過程最終會交由Realm執行,這時會調用Realm的 getAuthenticationInfo(token) 方法。
    該方法主要執行以下操作:

  • 檢查提交的進行認證的令牌信息
  • 根據令牌信息從數據源(通常為數據庫)中獲取用戶信息
  • 對用戶信息進行匹配驗證。
  • 驗證通過將返回一個封裝了用戶信息的AuthenticationInfo實例。
  • 驗證失敗則拋出AuthenticationException異常信息。
  • 而在我們的應用程序中要做的就是自定義一個Realm類,繼承 AuthorizingRealm 抽象類,重載 doGetAuthenticationInfo (),重寫獲取用戶信息的方法。

    @Component public class OAuth2Realm extends AuthorizingRealm {@Resourceprivate ShiroService shiroService;@Resourceprivate SysUserService sysUserService;/*** 此方法調用 hasRole,hasPermission的時候才會進行回調.** 權限信息.(授權):* 1、如果用戶正常退出,緩存自動清空;* 2、如果用戶非正常退出,緩存自動清空;* 3、如果我們修改了用戶的權限,而用戶不退出系統,修改的權限無法立即生效。* (需要手動編程進行實現;放在service進行調用)* 在權限修改后調用realm中的方法,realm已經由spring管理,所以從spring中獲取realm實例,* 調用clearCached方法;* :Authorization 是授權訪問控制,用于對用戶進行的操作授權,證明該用戶是否允許進行當前操作,如訪問某個鏈接,某個資源文件等。*** 當沒有使用緩存的時候,不斷刷新頁面的話,這個代碼會不斷執行,* 當其實沒有必要每次都重新設置權限信息,所以我們需要放到緩存中進行管理;* 當放到緩存中時,這樣的話,doGetAuthorizationInfo就只會執行一次了,* 緩存過期之后會再次執行。** @param principals* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {SysUserEntity user =(SysUserEntity) (principals.getPrimaryPrincipal());;// 獲取該用戶權限列表Set<String> permsSet = shiroService.getUserPermissions(user.getId());SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setStringPermissions(permsSet);return info;}/*** 認證回調函數,登錄時調用* 首先根據傳入的用戶名獲取User信息;然后如果user為空,那么拋出沒找到帳號異常UnknownAccountException;* 如果user找到但鎖定了拋出鎖定異常LockedAccountException;最后生成AuthenticationInfo信息,* 交給間接父類AuthenticatingRealm使用CredentialsMatcher進行判斷密碼是否匹配,*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;SysUserEntity user = sysUserService.queryByUsername(usernamePasswordToken.getUsername());//賬號不存在、密碼錯誤if (user == null) {throw new KCException("賬號或密碼不正確");}// 交給 shiro 自己去驗證,// 明文驗證return new SimpleAuthenticationInfo(user, // 存入憑證的信息,登陸成功后可以使用 SecurityUtils.getSubject().getPrincipal();在任何地方使用它user.getPassword(),getName());// 加密的方式// 交給AuthenticatingRealm使用CredentialsMatcher進行密碼匹配,如果覺得人家的不好可以自定義實現/*return new SimpleAuthenticationInfo(user,user.getPassword(),ByteSource.Util.bytes(user.getSalt()), // 加鹽,可以注冊憑證匹配器 HashedCredentialsMatcher 告訴它怎么加密的getName());*/} }

    實現上面兩個方法即可完成身份驗證和權限驗證。

    登陸實現

    @PostMapping("/login")@ApiOperation("系統登陸")public ResponseEntity<String> login(@RequestBody SysUserLoginForm userForm) {String kaptcha = ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY);if (!userForm.getCaptcha().equalsIgnoreCase(kaptcha)) {throw new KCException("驗證碼不正確!");}UsernamePasswordToken token = new UsernamePasswordToken(userForm.getUsername(), userForm.getPassword());Subject currentUser = SecurityUtils.getSubject();currentUser.login(token);//賬號鎖定if (getUser().getStatus() == SysConstant.SysUserStatus.LOCK) {throw new KCException("賬號已被鎖定,請聯系管理員");}return ResponseEntity.status(HttpStatus.OK).body("登陸成功!");}

    權限驗證

    @ApiOperation("用于測試,查詢")@ApiImplicitParam(name = "string", value = "id", dataType = "string")@RequiresPermissions("sys:user:list1")@GetMapping()public ResponseEntity<List<SysUserEntity>> query(@CustomValid String string) {return new ResponseEntity<>(sysUserService.query(new SysUserEntity()), OK);}

    簡單測試一個例子,sys:user:list1 多加一個 1 肯定會驗證失敗,查看程序會做什么,它會去我們定義的 Realm 中的 doGetAuthorizationInfo(PrincipalCollection principals) 方法中,執行查詢該用戶的所有權限。
    驗證失敗后最后程序結果如下:

    Caused by: org.apache.shiro.authz.AuthorizationException: Not authorized to invoke method: public org.springframework.http.ResponseEntity com.wuwii.module.sys.controller.SysUserController.query(java.lang.String)at org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor.assertAuthorized(AuthorizingAnnotationMethodInterceptor.java:90)... 77 common frames omitted
    權限注解
    @RequiresAuthentication 表示當前Subject已經通過login進行了身份驗證;即Subject. isAuthenticated()返回true@RequiresUser 表示當前Subject已經身份驗證或者通過記住我登錄的。@RequiresGuest 表示當前Subject沒有身份驗證或通過記住我登錄過,即是游客身份。@RequiresRoles(value={“admin”, “user”}, logical= Logical.AND) 表示當前Subject需要角色admin和user。@RequiresPermissions (value={“user:a”, “user:b”}, logical= Logical.OR) 表示當前Subject需要權限user:a或user:b。

    參考資料

    • [Spring Boot 集成-Shiro]
    • 39.2. Spring Boot Shiro權限管理【從零開始學Spring Boot】

  • Role-Based Access Control ?
  • 總結

    以上是生活随笔為你收集整理的学习Spring Boot:(十三)配置 Shiro 权限认证的全部內容,希望文章能夠幫你解決所遇到的問題。

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