Springboot 快速集成 Shiro权限管理

Posted 林深时见鹿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Springboot 快速集成 Shiro权限管理相关的知识,希望对你有一定的参考价值。

SpringBoot2.0集成Shiro

数据库表结构:

 

 1 /*
 2  Navicat Premium Data Transfer
 3 
 4  Source Server         : 本机mysql
 5  Source Server Type    : MySQL
 6  Source Server Version : 50527
 7  Source Host           : localhost:3306
 8  Source Schema         : shiro3
 9 
10  Target Server Type    : MySQL
11  Target Server Version : 50527
12  File Encoding         : 65001
13 
14  Date: 30/06/2020 16:49:48
15 */
16 
17 SET NAMES utf8mb4;
18 SET FOREIGN_KEY_CHECKS = 0;
19 
20 -- ----------------------------
21 -- Table structure for sys_permissions
22 -- ----------------------------
23 DROP TABLE IF EXISTS `sys_permissions`;
24 CREATE TABLE `sys_permissions`  (
25   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT \'编号\',
26   `permission` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT \'权限编号\',
27   `description` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT \'权限描述\',
28   `rid` bigint(20) NULL DEFAULT NULL COMMENT \'此权限关联角色的id\',
29   `available` tinyint(1) NULL DEFAULT 0 COMMENT \'是否锁定\',
30   PRIMARY KEY (`id`) USING BTREE,
31   UNIQUE INDEX `idx_sys_permissions_permission`(`permission`) USING BTREE
32 ) ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
33 
34 -- ----------------------------
35 -- Table structure for sys_roles
36 -- ----------------------------
37 DROP TABLE IF EXISTS `sys_roles`;
38 CREATE TABLE `sys_roles`  (
39   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT \'角色编号\',
40   `role` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT \'角色名称\',
41   `description` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT \'角色描述\',
42   `pid` bigint(20) NULL DEFAULT NULL COMMENT \'父节点\',
43   `available` tinyint(1) NULL DEFAULT 0 COMMENT \'是否锁定\',
44   PRIMARY KEY (`id`) USING BTREE,
45   UNIQUE INDEX `idx_sys_roles_role`(`role`) USING BTREE
46 ) ENGINE = InnoDB AUTO_INCREMENT = 22 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
47 
48 -- ----------------------------
49 -- Table structure for sys_roles_permissions
50 -- ----------------------------
51 DROP TABLE IF EXISTS `sys_roles_permissions`;
52 CREATE TABLE `sys_roles_permissions`  (
53   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT \'编号\',
54   `role_id` bigint(20) NULL DEFAULT NULL COMMENT \'角色编号\',
55   `permission_id` bigint(20) NULL DEFAULT NULL COMMENT \'权限编号\',
56   PRIMARY KEY (`id`) USING BTREE
57 ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
58 
59 -- ----------------------------
60 -- Table structure for sys_users
61 -- ----------------------------
62 DROP TABLE IF EXISTS `sys_users`;
63 CREATE TABLE `sys_users`  (
64   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT \'编号\',
65   `username` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT \'用户名\',
66   `password` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT \'密码\',
67   `salt` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT \'盐值\',
68   `role_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT \'角色列表\',
69   `locked` tinyint(1) NULL DEFAULT 0 COMMENT \'是否锁定\',
70   PRIMARY KEY (`id`) USING BTREE,
71   UNIQUE INDEX `idx_sys_users_username`(`username`) USING BTREE
72 ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
73 
74 -- ----------------------------
75 -- Table structure for sys_users_roles
76 -- ----------------------------
77 DROP TABLE IF EXISTS `sys_users_roles`;
78 CREATE TABLE `sys_users_roles`  (
79   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT \'编号\',
80   `user_id` bigint(20) NULL DEFAULT NULL COMMENT \'用户编号\',
81   `role_id` bigint(20) NULL DEFAULT NULL COMMENT \'角色编号\',
82   PRIMARY KEY (`id`) USING BTREE
83 ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
84 
85 SET FOREIGN_KEY_CHECKS = 1;

 

 

 

项目版本:

 1 springboot2.x 2 shiro:1.3.2 

Maven依赖:

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>

  

springboot中集成shiro相对简单,只需要两个类:一个是shiroConfig类,一个是CustonRealm类。

ShiroConfig类:
顾名思义就是对shiro的一些配置,相对于之前的xml配置。包括:过滤的文件和权限,密码加密的算法,其用注解等相关功能。

CustomRealm类:
自定义的CustomRealm继承AuthorizingRealm。并且重写父类中的doGetAuthorizationInfo(权限相关)、doGetAuthenticationInfo(身份认证)这两个方法

项目结构:

 

shiroConfig配置:

  1 package com.SC.demo.utils;
  2 
  3 import java.util.LinkedHashMap;
  4 import java.util.Map;
  5 
  6 import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
  7 import org.apache.shiro.mgt.SecurityManager;
  8 import org.apache.shiro.spring.LifecycleBeanPostProcessor;
  9 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
 10 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
 11 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
 12 import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
 13 import org.springframework.beans.factory.annotation.Autowired;
 14 import org.springframework.context.annotation.Bean;
 15 import org.springframework.context.annotation.Configuration;
 16 import org.springframework.context.annotation.DependsOn;
 17 
 18 import com.SC.demo.realm.CustomRealm;
 19 
 20 /**
 21  * 描述:
 22  *
 23  * @author caojing
 24  * @create 2019-01-27-13:38
 25  */
 26 @Configuration
 27 public class ShiroConfig {
 28     
 29 //    @Autowired
 30 //    CustomRealm customRealm;
 31 
 32     @Bean(name = "shiroFilter")
 33     public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
 34         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
 35         shiroFilterFactoryBean.setSecurityManager(securityManager);
 36         shiroFilterFactoryBean.setLoginUrl("/login");
 37         shiroFilterFactoryBean.setUnauthorizedUrl("/notRole");
 38         Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
 39         // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
 40         filterChainDefinitionMap.put("/webjars/**", "anon");
 41         filterChainDefinitionMap.put("/login", "anon");
 42         filterChainDefinitionMap.put("/", "anon");
 43         filterChainDefinitionMap.put("/front/**", "anon");
 44         filterChainDefinitionMap.put("/api/**", "anon");
 45 
 46         filterChainDefinitionMap.put("/admin/**", "authc");
 47         filterChainDefinitionMap.put("/user/**", "authc");
 48         //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
 49         filterChainDefinitionMap.put("/**", "authc");
 50         shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
 51         return shiroFilterFactoryBean;
 52 
 53     }
 54 
 55     @Bean
 56     public SecurityManager securityManager() {
 57         DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
 58         defaultSecurityManager.setRealm(customRealm());
 59         return defaultSecurityManager;
 60     }
 61 
 62     @Bean
 63     public CustomRealm customRealm() {
 64         CustomRealm customRealm = new CustomRealm();
 65         // 告诉realm,使用credentialsMatcher加密算法类来验证密文
 66         customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
 67         customRealm.setCachingEnabled(false);
 68         return customRealm;
 69     }
 70 
 71     
 72     @Bean
 73     public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
 74         return new LifecycleBeanPostProcessor();
 75     }
 76     
 77  /**
 78      * *
 79      * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
 80      * *
 81      * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
 82      * * @return
 83      */
 84     @Bean
 85     @DependsOn({"lifecycleBeanPostProcessor"})
 86     public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
 87         DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
 88         advisorAutoProxyCreator.setProxyTargetClass(true);
 89         return advisorAutoProxyCreator;
 90     }
 91     
 92     @Bean
 93     public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
 94         AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
 95         authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
 96         return authorizationAttributeSourceAdvisor;
 97     }
 98     
 99     //shiro 加密配置
100     @Bean(name = "credentialsMatcher")
101     public HashedCredentialsMatcher hashedCredentialsMatcher() {
102         HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
103         // 散列算法:这里使用MD5算法;
104         hashedCredentialsMatcher.setHashAlgorithmName("md5");
105         // 散列的次数,比如散列两次,相当于 md5(md5(""));
106         hashedCredentialsMatcher.setHashIterations(2);
107         // storedCredentialsHexEncoded默认是true,此时用的是密码加密用的是Hex编码;false时用Base64编码
108         hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
109         return hashedCredentialsMatcher;
110     }
111 
112 
113 }

 

 

 

shiro的三个核心概念:

Subject: 代表当前正在执行操作的用户,但Subject代表的可以是人,也可以是任何第三方系统帐号。当然每个subject实例都会被绑定到SercurityManger上。
SecurityManger:SecurityManager是Shiro核心,主要协调Shiro内部的各种安全组件,这个我们不需要太关注,只需要知道可以设置自定的Realm。
Realm:用户数据和Shiro数据交互的桥梁。比如需要用户身份认证、权限认证。都是需要通过Realm来读取数据。
shiroFilter方法:
这个方法看名字就知道了:shiro的过滤器,可以设置登录页面(setLoginUrl)、权限不足跳转页面(setUnauthorizedUrl)、具体某些页面的权限控制或者身份认证。
注意:这里是需要设置SecurityManager(setSecurityManager)。
默认的过滤器还有:anno、authc、authcBasic、logout、noSessionCreation、perms、port、rest、roles、ssl、user过滤器。
具体的大家可以查看package org.apache.shiro.web.filter.mgt.DefaultFilter。这个类,常用的也就authc、anno。
securityManager 方法:
查看源码可以知道 securityManager是一个接口类,我们可以看下它的实现类:

具体怎么实现的,感兴趣的同学可以看下。由于项目是一个web项目,所以我们使用的是DefaultWebSecurityManager ,然后设置自己的Realm


CustomRealm 方法:

将 customRealm的实例化交给spring去管理,当然这里也可以利用注解的方式去注入。

 1 package com.SC.demo.realm;
 2 
 3 import org.apache.shiro.SecurityUtils;
 4 import org.apache.shiro.authc.*;
 5 import org.apache.shiro.authz.AuthorizationInfo;
 6 import org.apache.shiro.authz.SimpleAuthorizationInfo;
 7 import org.apache.shiro.realm.AuthorizingRealm;
 8 import org.apache.shiro.subject.PrincipalCollection;
 9 import org.apache.shiro.subject.Subject;
10 import org.apache.shiro.util.ByteSource;
11 import org.springframework.beans.factory.annotation.Autowired;
12 import org.springframework.context.annotation.Bean;
13 import org.springframework.context.annotation.Configuration;
14 import org.springframework.stereotype.Component;
15 
16 import com.SC.demo.pojo.Sys_Roles;
17 import com.SC.demo.pojo.Sys_Users;
18 import com.SC.demo.pojo.Sys_permissions;
19 import com.SC.demo.service.serviceimp.UserServiceImp;
20 
21 import java.util.HashSet;
22 import java.util.Set;
23 
24 /**
25 * @author 作者 :Runaway programmer
26 * @version 创建时间:2019年12月18日 下午4:48:28
27 * 类说明
28 */
29 
30 public class CustomRealm extends AuthorizingRealm {
31     
32     @Autowired
33     UserServiceImp userServiceImp;
34 
35     @Override
36     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
37         String username = (String) SecurityUtils.getSubject().getPrincipal();
38         SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
39         //从数据库中获取当前用户拥有的权限  存入集合中 再存入SimpleAuthorizationInfo对象里供注解获取对比权限
40         Sys_permissions permission = userServiceImp.getUserRole(username);
41         Set<String> stringSet = new HashSet<>();
42         stringSet.add(permission.getPermission());
43         stringSet.add("user:admin");
44         info.setStringPermissions(stringSet);
45         return info;
46     }
47 
48     /**
49      * 这里可以注入userService,为了方便演示,我就写死了帐号了密码
50      * private UserService userService;
51      * <p>
52      * 获取即将需要认证的信息
53      */
54     @Override
55     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
56         System.out.println("-------身份认证方法--------");
57         String userName = (String) authenticationToken.getPrincipal();
58         System.out.println(userName);
59         String userPwd = new String((char[]) authenticationToken.getCredentials());
60         
61         //根据用户名从数据库获取密码
62         Sys_Users userNamePassword = userServiceImp.getUserNamePassword(userName);
63         String password = userNamePassword.getPassword();
64         String salt = userNamePassword.getSalt();
65 //        String password = "4b91fc877a3f4df7e812f98ebde4b5e5";
66         if (userName == null) {
67             throw new AccountException("用户名不正确");
68         } else if (!userPwd.equals(userPwd)) {
69             throw new AccountException("密码不正确");
70         }
71         //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配
72         //1.当前用户名userName 2.以及数据库查询password 3.盐 (设置的盐 userName+Salt)4.当前realm对象
73         return new SimpleAuthenticationInfo(userName, password,
74                 ByteSource.Util.bytes(userName + salt), getName());
75 
76     }
77 }

 

说明:
自定义的Realm类继承AuthorizingRealm类,并且重载doGetAuthorizationInfo和doGetAuthenticationInfo两个方法。
doGetAuthorizationInfo: 权限认证,即登录过后,每个身份不一定,对应的所能看的页面也不一样。
doGetAuthenticationInfo:身份认证。即登录通过账号和密码验证登陆人的身份信息。

 

controller类:

 

 1 package com.SC.demo.controller;
 2 
 3 import org.apache.shiro.SecurityUtils;
 4 import org.apache.shiro.authc.AuthenticationException;
 5 import org.apache.shiro.authc.ExcessiveAttemptsException;
 6 import org.apache.shiro.authc.IncorrectCredentialsException;
 7 import org.apache.shiro.authc.LockedAccountException;
 8 import org.apache.shiro.authc.UnknownAccountException;
 9 import org.apache.shiro.authc.UsernamePasswordToken;
10 import org.apache.shiro.subject.Subject;
11 import org.springframework.web.bind.annotation.RequestMapping;
12 import org.springframework.web.bind.annotation.RequestMethod;
13 import org.springframework.web.bind.annotation.RequestParam;
14 import org.springframework.web.bind.annotation.ResponseBody;
15 import org.springframework.web.bind.annotation.RestController;
16 
17 import com.SC.demo.utils.MD5utils;
18 
19 /**
20 * @author 作者 :Runaway programmer
21 * @version 创建时间:2019年12月18日 下午4:49:50
22 * 类说明
23 */
24 @RestController
25 public class HomeIndexController {
26 
27     @RequestMapping(value = "/login", method = RequestMethod.GET)
28     @ResponseBody
29     public String defaultLogin() {
30         return "首页";
31     }
32 
33     @RequestMapping(value = "/login", method = RequestMethod.POST)
34     @ResponseBody
35     public String login(@RequestParam("username") String username, @RequestParam("password") String password) {
36         // 从SecurityUtils里边创建一个 subject
37         Subject subject = SecurityUtils.getSubject();
38         // 在认证提交前准备 token(令牌)
39         UsernamePasswordToken token = new UsernamePasswordToken(username, password);
40         //MD5加盐获取值
41         String md5Pwd = MD5utils.MD5Pwd(username, password);
42         System.out.println(md5Pwd);
43         // 执行认证登陆
44         try {
45             subject.login(token);
46         } catch (UnknownAccountException uae) {
47             return "未知账户";
48         } catch (IncorrectCredentialsException ice) {
49             return "密码不正确";
50         } catch (LockedAccountException lae) {
51             return "账户已锁定";
52         } catch (ExcessiveAttemptsException eae) {
53             return "用户名或密码错误次数过多";
54         } catch (AuthenticationException ae) {
55             return "用户名或密码不正确!";
56         }
57         if (subject.isAuthenticated()) {
58             return "登录成功";
59         } else {
60             token.clear();
61             return "登录失败";
62         }
63     }
64 
65 }

 

 

利用注解配置权限:
其实,我们完全可以不用注解的形式去配置权限,因为在之前已经加过了:DefaultFilter类中有perms(类似于perms[user:add])这种形式的。但是试想一下,这种控制的粒度可能会很细,具体到某一个类中的方法,那么如果是配置文件配,是不是每个方法都要加一个perms?但是注解就不一样了,直接写在方法上面,简单快捷。
很简单,主需要在config类中加入如下代码,就能开启注解:

 1     @Bean
 2     public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
 3         return new LifecycleBeanPostProcessor();
 4     }
 5     
 6  /**
 7      * *
 8      * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
 9      * *
10      * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
11      * * @return
12      */
13     @Bean
14     @DependsOn({"lifecycleBeanPostProcessor"})
15     public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
16         DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
17         advisorAutoProxyCreator.setProxyTargetClass(true);
18         return advisorAutoProxyCreator;
19     }
20     
21     @Bean
22     public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
23         AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
24         authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
25         return authorizationAttributeSourceAdvisor;
26     }
27     
28     //shiro 加密配置
29     @Bean(name = "credentialsMatcher")
30     public HashedCredentialsMatcher hashedCredentialsMatcher() {
31         HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
32         // 散列算法:这里使用MD5算法;
33         hashedCredentialsMatcher.setHashAlgorithmName("md5");
34         // 散列的次数,比如散列两次,相当于 md5(md5(""));
35         hashedCredentialsMatcher.setHashIterations(2);
36         // storedCredentialsHexEncoded默认是true,此时用的是密码加密用的是Hex编码;false时用Base64编码
37         hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
38         return hashedCredentialsMatcher;
39     }

 

 

注解权限Controller测试类

 1 package com.SC.demo.controller;
 2 
 3 import org.apache.shiro.authz.annotation.RequiresPermissions;
 4 import org.apache.shiro.authz.annotation.RequiresRoles;
 5 import org.springframework.stereotype.Controller;
以上是关于Springboot 快速集成 Shiro权限管理的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot 集成 Shiro:使用Shiro的权限管理

SpringBoot11:集成Shiro

SpringBoot11:集成Shiro

Spring boot 入门:集成 Shiro 实现登陆认证和权限管理

springboot mybatis 后台框架平台 集成代码生成器 shiro 权限

SpringBoot集成Shiro