您好,登錄后才能下訂單哦!
序
在實際項目中,經常需要用到角色權限區分,以此來為不同的角色賦予不同的權利,分配不同的任務。比如,普通用戶只能瀏覽;會員可以瀏覽和評論;超級會員可以瀏覽、評論和看視頻課等;實際應用場景很多。毫不夸張的說,幾乎每個完整的項目都會設計到權限管理。
在 Spring Boot 中做權限管理,一般來說,主流的方案是 Spring Security ,但是由于 Spring Security 過于龐大和復雜,只要能滿足業務需要,大多數公司還是會選擇 Apache Shiro 來使用。
一般來說,Spring Security 和 Shiro 的區別如下:
Spring Security | Apache Shiro |
---|---|
重量級的安全管理框架 | 輕量級的安全管理框架 |
概念復雜,配置繁瑣 | 概念簡單、配置簡單 |
功能強大 | 功能簡單 |
因此,這篇文章,阿淼首先會帶大家了解 Apache Shiro ,然后和大家一起將 shiro 權限框架整合到 SpringBoot 中,以達到快速的實現整合權限管理的功能。
走進 Apache Shiro
官網認知
照例又去官網扒了扒介紹:
Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro's easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.
Apache Shiro™是一個強大且易用的Java安全框架,能夠用于身份驗證、授權、加密和會話管理。Shiro擁有易于理解的API,您可以快速、輕松地獲得任何應用程序——從最小的移動應用程序到最大的網絡和企業應用程序。
簡而言之,Apache Shiro 是一個強大靈活的開源安全框架,可以完全處理身份驗證、授權、加密和會話管理。
Shiro能到底能做些什么呢?
為什么今天還要使用Apache Shiro?
對此,官方給出了詳細的解釋:http://shiro.apache.org/
自2003年以來,框架環境發生了很大變化,因此今天仍然有充分的理由使用Shiro。實際上有很多原因。Apache Shiro是:
Shiro 核心概念
Apache Shiro 是一個全面的、蘊含豐富功能的安全框架。
下圖為描述 Shiro 功能的框架圖:
如圖所示,功能包括:
并且 Shiro 還有通過增加其他的功能來支持和加強這些不同應用環境下安全領域的關注點。
特別是對以下的功能支持:
注意: Shiro 不會去維護用戶、維護權限,這些需要我們自己去設計/提供,然后通過相應的接口注入給 Shiro
使用案例 Demo
1.新建 maven 項目
為方便我們初始化項目,Spring Boot給我們提供一個項目模板生成網站。
1、打開瀏覽器,訪問:https://start.spring.io/
2、根據頁面提示,選擇構建工具,開發語言,項目信息等。
2.導入 springboot 父依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> </parent>
3.相關 jar 包
web 包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
shiro-spring 包就是此篇文章的核心
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> shiro 注解會用到 aop <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> 數據庫相關包使用的是mybatisplus <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.0</version> </dependency>
4.數據庫
建表語句在項目中有,項目地址: https://github.com/mmzsblog/mmzsblog-util
5.自定義 realm
public class MyShiroRealm extends AuthorizingRealm { @Autowired private UserService userService; @Autowired private RoleService roleService; @Autowired private PermissionService permissionService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); // HttpServletRequest request = (HttpServletRequest) ((WebSubject) SecurityUtils // .getSubject()).getServletRequest();//這個可以用來獲取在登錄的時候提交的其他額外的參數信息 String username = (String) principals.getPrimaryPrincipal(); // 受理權限 // 角色 Set<String> roles = new HashSet<String>(); Role role = roleService.getRoleByUserName(username); System.out.println(role.getRoleName()); roles.add(role.getRoleName()); authorizationInfo.setRoles(roles); // 權限 Set<String> permissions = new HashSet<String>(); List<Permission> querypermissions = permissionService.getPermissionsByRoleId(role.getId()); for (Permission permission : querypermissions) { permissions.add(permission.getPermissionName()); } authorizationInfo.setStringPermissions(permissions); return authorizationInfo; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { String loginName = (String) authcToken.getPrincipal(); // 獲取用戶密碼 User user = userService.getOne(new QueryWrapper<User>().eq("username", loginName)); if (user == null) { // 沒找到帳號 throw new UnknownAccountException(); } String password = new String((char[]) authcToken.getCredentials()); String inpass = (new Md5Hash(password, user.getUsername())).toString(); if (!user.getPassword().equals(inpass)) { throw new IncorrectCredentialsException(); } // 交給AuthenticatingRealm使用CredentialsMatcher進行密碼匹配 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(loginName, user.getPassword(), ByteSource.Util.bytes(loginName), getName()); return authenticationInfo; } }
6.shiro 配置類
@Configuration public class ShiroConfiguration { private static final Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class); /** * Shiro的Web過濾器Factory 命名:shiroFilter */ @Bean(name = "shiroFilter") public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // Shiro的核心安全接口,這個屬性是必須的 shiroFilterFactoryBean.setSecurityManager(securityManager); //需要權限的請求,如果沒有登錄則會跳轉到這里設置的url shiroFilterFactoryBean.setLoginUrl("/login.html"); //設置登錄成功跳轉url,一般在登錄成功后自己代碼設置跳轉url,此處基本沒用 shiroFilterFactoryBean.setSuccessUrl("/main.html"); //設置無權限跳轉界面,此處一般不生效,一般自定義異常 shiroFilterFactoryBean.setUnauthorizedUrl("/error.html"); Map<String, Filter> filterMap = new LinkedHashMap<>(); // filterMap.put("authc", new AjaxPermissionsAuthorizationFilter()); shiroFilterFactoryBean.setFilters(filterMap); /* * 定義shiro過濾鏈 Map結構 * Map中key(xml中是指value值)的第一個'/'代表的路徑是相對于HttpServletRequest.getContextPath()的值來的 * anon:它對應的過濾器里面是空的,什么都沒做,這里.do和.jsp后面的*表示參數,比方說login.jsp?main這種 * authc:該過濾器下的頁面必須驗證后才能訪問,它是Shiro內置的一個攔截器org.apache.shiro.web.filter.authc. * FormAuthenticationFilter */ Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); /* * 過濾鏈定義,從上向下順序執行,一般將 /**放在最為下邊; authc:所有url都必須認證通過才可以訪問; * anon:所有url都都可以匿名訪問 */ filterChainDefinitionMap.put("/login.html", "authc"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /** * 權限管理 */ @Bean public SecurityManager securityManager() { logger.info("=======================shiro======================="); DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(MyShiroRealm()); // securityManager.setRememberMeManager(rememberMeManager); return securityManager; } /** * Shiro Realm 繼承自AuthorizingRealm的自定義Realm,即指定Shiro驗證用戶登錄的類為自定義的 */ @Bean public MyShiroRealm MyShiroRealm() { MyShiroRealm userRealm = new MyShiroRealm(); userRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return userRealm; } /** * 憑證匹配器 密碼驗證 */ @Bean(name = "credentialsMatcher") public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); // 散列算法:這里使用MD5算法; hashedCredentialsMatcher.setHashAlgorithmName("md5"); // 散列的次數,比如散列兩次,相當于 md5(md5("")); hashedCredentialsMatcher.setHashIterations(1); // storedCredentialsHexEncoded默認是true,此時用的是密碼加密用的是Hex編碼;false時用Base64編碼 hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true); return hashedCredentialsMatcher; } /** * 開啟Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP掃描使用Shiro注解的類,并在必要時進行安全邏輯驗證 */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); return authorizationAttributeSourceAdvisor; } }
7.測試類
@RestController public class UserController { @PostMapping("login") public String name(String username, String password) { String result = "已登錄"; Subject currentUser = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); if (!currentUser.isAuthenticated()) { try { currentUser.login(token);// 會觸發com.shiro.config.MyShiroRealm的doGetAuthenticationInfo方法 result = "登錄成功"; } catch (UnknownAccountException e) { result = "用戶名錯誤"; } catch (IncorrectCredentialsException e) { result = "密碼錯誤"; } } return result; } @GetMapping("logout") public void logout() { Subject currentUser = SecurityUtils.getSubject(); currentUser.logout(); } @RequiresPermissions("role:update") @GetMapping("/role") public String name() { return "hello"; } @RequiresPermissions("user:select") @GetMapping("/role2") public String permission() { return "hello sel"; } }
7.1 登錄測試
數據庫賬號(密碼經過md5加鹽加密)
7.2 權限測試
8.說明
8.1 無權限時的處理
無權限時自定義了一個異常。所以,權限測試的時候沒有權限就會提示配置的提示語 “沒有權限”。
@ControllerAdvice public class ShiroException { @ExceptionHandler(value = UnauthorizedException.class) @ResponseBody public String name() { return "沒有權限"; } }
8.2 角色權限測試與權限測試相同
權限設置可在shiro配置類中shiro過濾鏈設置,也可用注解方式設置,本文使用注解方式。
8.3 shiro 的 session 和 cache
shiro 的 session 和 cache 管理可以自定義,本文用的是默認的,推薦自定義,方便管理。
小結
參考:
http://shiro.apache.org/
https://www.cnblogs.com/joker-dj/archive/2020/04/13/12690648.html
到此這篇關于springboot2.x整合shiro權限框架的使用的文章就介紹到這了,更多相關springboot2.x整合shiro內容請搜索億速云以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持億速云!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。