1 Shiro整合Spirngboot 项目数据库表

image-20190816163721705

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/*
Navicat MySQL Data Transfer

Source Server : localhost
Source Server Version : 50558
Source Host : localhost:3306
Source Database : shiro_db

Target Server Type : MYSQL
Target Server Version : 50558
File Encoding : 65001

Date: 2018-11-29 18:03:45
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for pe_permission
-- ----------------------------
DROP TABLE IF EXISTS `pe_permission`;
CREATE TABLE `pe_permission` (
`id` varchar(40) NOT NULL COMMENT '主键',
`name` varchar(255) DEFAULT NULL COMMENT '权限名称',
`code` varchar(20) DEFAULT NULL,
`description` text COMMENT '权限描述',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of pe_permission
-- ----------------------------
INSERT INTO `pe_permission` VALUES ('1', '添加用户', 'user-add', null);
INSERT INTO `pe_permission` VALUES ('2', '查询用户', 'user-find', null);
INSERT INTO `pe_permission` VALUES ('3', '更新用户', 'user-update', null);
INSERT INTO `pe_permission` VALUES ('4', '删除用户', 'user-delete', null);

-- ----------------------------
-- Table structure for pe_role
-- ----------------------------
DROP TABLE IF EXISTS `pe_role`;
CREATE TABLE `pe_role` (
`id` varchar(40) NOT NULL COMMENT '主键ID',
`name` varchar(40) DEFAULT NULL COMMENT '权限名称',
`description` varchar(255) DEFAULT NULL COMMENT '说明',
PRIMARY KEY (`id`),
UNIQUE KEY `UK_k3beff7qglfn58qsf2yvbg41i` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of pe_role
-- ----------------------------
INSERT INTO `pe_role` VALUES ('1', '系统管理员', '系统日常维护');
INSERT INTO `pe_role` VALUES ('2', '普通员工', '普通操作权限');

-- ----------------------------
-- Table structure for pe_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `pe_role_permission`;
CREATE TABLE `pe_role_permission` (
`role_id` varchar(40) NOT NULL COMMENT '角色ID',
`permission_id` varchar(40) NOT NULL COMMENT '权限ID',
PRIMARY KEY (`role_id`,`permission_id`),
KEY `FK74qx7rkbtq2wqms78gljv87a0` (`permission_id`),
KEY `FKee9dk0vg99shvsytflym6egxd` (`role_id`),
CONSTRAINT `fk-p-rid` FOREIGN KEY (`role_id`) REFERENCES `pe_role` (`id`),
CONSTRAINT `fk-pid` FOREIGN KEY (`permission_id`) REFERENCES `pe_permission` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of pe_role_permission
-- ----------------------------
INSERT INTO `pe_role_permission` VALUES ('1', '1');
INSERT INTO `pe_role_permission` VALUES ('1', '2');
INSERT INTO `pe_role_permission` VALUES ('2', '2');
INSERT INTO `pe_role_permission` VALUES ('1', '3');
INSERT INTO `pe_role_permission` VALUES ('1', '4');

-- ----------------------------
-- Table structure for pe_user
-- ----------------------------
DROP TABLE IF EXISTS `pe_user`;
CREATE TABLE `pe_user` (
`id` varchar(40) NOT NULL COMMENT 'ID',
`username` varchar(255) NOT NULL COMMENT '用户名称',
`password` varchar(255) DEFAULT NULL COMMENT '密码',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of pe_user
-- ----------------------------
INSERT INTO `pe_user` VALUES ('1', 'zhangsan', '123456');
INSERT INTO `pe_user` VALUES ('2', 'lisi', '123456');
INSERT INTO `pe_user` VALUES ('3', 'wangwu', '123456');

-- ----------------------------
-- Table structure for pe_user_role
-- ----------------------------
DROP TABLE IF EXISTS `pe_user_role`;
CREATE TABLE `pe_user_role` (
`role_id` varchar(40) NOT NULL COMMENT '角色ID',
`user_id` varchar(40) NOT NULL COMMENT '权限ID',
KEY `FK74qx7rkbtq2wqms78gljv87a1` (`role_id`),
KEY `FKee9dk0vg99shvsytflym6egx1` (`user_id`),
CONSTRAINT `fk-rid` FOREIGN KEY (`role_id`) REFERENCES `pe_role` (`id`),
CONSTRAINT `fk-uid` FOREIGN KEY (`user_id`) REFERENCES `pe_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of pe_user_role
-- ----------------------------
INSERT INTO `pe_user_role` VALUES ('1', '1');

2 修改登录方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 /**
* 1.传统登录
* 前端发送登录请求 => 接口部分获取用户名密码 => 程序员在接口部分手动控制
* 2.shiro登录
* 前端发送登录请求 => 接口部分获取用户名密码 => 通过subject.login => realm域的认证方法
*
*/
//用户登录
@RequestMapping(value="/login")
public String login(String username,String password) {
//构造登录令牌
try {

/**
* 密码加密:
* shiro提供的md5加密
* Md5Hash:
* 参数一:加密的内容
* 111111 --- abcd
* 参数二:盐(加密的混淆字符串)(用户登录的用户名)
* 111111+混淆字符串
* 参数三:加密次数
*
*/
password = new Md5Hash(password,username,3).toString();

UsernamePasswordToken upToken = new UsernamePasswordToken(username,password);
//1.获取subject
Subject subject = SecurityUtils.getSubject();

//获取session
String sid = (String) subject.getSession().getId();

//2.调用subject进行登录
subject.login(upToken);
return "登录成功";
}catch (Exception e) {
return "用户名或密码错误";
}
}

3 自定义realm:认证的操作步骤分析.

image-20190816170032579

image-20190816170107883


Realm域:Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么 它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行 验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package cn.itcast.shiro.realm;

import cn.itcast.shiro.domain.Permission;
import cn.itcast.shiro.domain.Role;
import cn.itcast.shiro.domain.User;
import cn.itcast.shiro.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.Set;

/**
* 自定义的realm
*/
public class CustomRealm extends AuthorizingRealm {

public void setName(String name) {
super.setName("customRealm");
}

@Autowired
private UserService userService;

/**
* 授权方法
* 操作的时候,判断用户是否具有响应的权限
* 先认证 -- 安全数据
* 再授权 -- 根据安全数据获取用户具有的所有操作权限
*
*
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//1.获取已认证的用户数据
User user = (User) principalCollection.getPrimaryPrincipal();//得到唯一的安全数据
//2.根据用户数据获取用户的权限信息(所有角色,所有权限)
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> roles = new HashSet<>();//所有角色
Set<String> perms = new HashSet<>();//所有权限
for (Role role : user.getRoles()) {
roles.add(role.getName());
for (Permission perm : role.getPermissions()) {
perms.add(perm.getCode());
}
}
info.setStringPermissions(perms);
info.setRoles(roles);
return info;
}


/**
* 认证方法
* 参数:传递的用户名密码
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//1.获取登录的用户名密码(token)
UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;
String username = upToken.getUsername();
String password = new String( upToken.getPassword());
//2.根据用户名查询数据库
User user = userService.findByName(username);
//3.判断用户是否存在或者密码是否一致
if(user != null && user.getPassword().equals(password)) {
//4.如果一致返回安全数据
//构造方法:安全数据,密码,realm域名
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
return info;
}
//5.不一致,返回null(抛出异常)
return null;
}


public static void main(String[] args) {
System.out.println(new Md5Hash("123456","wangwu",3).toString());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package cn.itcast.shiro.service;

import cn.itcast.shiro.dao.UserDao;
import cn.itcast.shiro.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {

@Autowired
private UserDao userDao;

public User findByName(String name) {
return this.userDao.findByUsername(name);
}

public List<User> findAll() {
return userDao.findAll();
}
}

Shiro配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package cn.itcast.shiro;

import cn.itcast.shiro.realm.CustomRealm;
import cn.itcast.shiro.session.CustomSessionManager;
import org.apache.shiro.mgt.SecurityManager;
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.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfiguration {

//1.创建realm
@Bean
public CustomRealm getRealm() {
return new CustomRealm();
}

//2.创建安全管理器
@Bean
public SecurityManager getSecurityManager(CustomRealm realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);

//将自定义的会话管理器注册到安全管理器中
securityManager.setSessionManager(sessionManager());
//将自定义的redis缓存管理器注册到安全管理器中
securityManager.setCacheManager(cacheManager());

return securityManager;
}

//3.配置shiro的过滤器工厂

/**
* 再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制
*
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
//1.创建过滤器工厂
ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
//2.设置安全管理器
filterFactory.setSecurityManager(securityManager);
//3.通用配置(跳转登录页面,为授权跳转的页面)
filterFactory.setLoginUrl("/autherror?code=1");//跳转url地址
filterFactory.setUnauthorizedUrl("/autherror?code=2");//未授权的url
//4.设置过滤器集合

/**
* 设置所有的过滤器:有顺序map
* key = 拦截的url地址
* value = 过滤器类型
*
*/
Map<String,String> filterMap = new LinkedHashMap<>();
//filterMap.put("/user/home","anon");//当前请求地址可以匿名访问

//具有某中权限才能访问
//使用过滤器的形式配置请求地址的依赖权限
//filterMap.put("/user/home","perms[user-home]"); //不具备指定的权限,跳转到setUnauthorizedUrl地址

//使用过滤器的形式配置请求地址的依赖角色
//filterMap.put("/user/home","roles[系统管理员]");

filterMap.put("/user/**","authc");//当前请求地址必须认证之后可以访问

filterFactory.setFilterChainDefinitionMap(filterMap);

return filterFactory;
}


@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;

/**
* 1.redis的控制器,操作redis
*/
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
return redisManager;
}

/**
* 2.sessionDao
*/
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO sessionDAO = new RedisSessionDAO();
sessionDAO.setRedisManager(redisManager());
return sessionDAO;
}

/**
* 3.会话管理器
*/
public DefaultWebSessionManager sessionManager() {
CustomSessionManager sessionManager = new CustomSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}

/**
* 4.缓存管理器
*/
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}




//开启对shior注解的支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}

shiro过滤器

image-20190816205018989

image-20190816205104582


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//4.设置过滤器集合

/**
* 设置所有的过滤器:有顺序map
* key = 拦截的url地址
* value = 过滤器类型
*
*/
Map<String,String> filterMap = new LinkedHashMap<>();
//filterMap.put("/user/home","anon");//当前请求地址可以匿名访问

//具有某中权限才能访问
//使用过滤器的形式配置请求地址的依赖权限
//filterMap.put("/user/home","perms[user-home]"); //不具备指定的权限,跳转到setUnauthorizedUrl地址

//使用过滤器的形式配置请求地址的依赖角色
//filterMap.put("/user/home","roles[系统管理员]");

filterMap.put("/user/**","authc");//当前请求地址必须认证之后可以访问

filterFactory.setFilterChainDefinitionMap(filterMap);

return filterFactory;

1
2
3
4
5
6
  @Bean
public OpenEntityManagerInViewFilter openEntityManagerInViewFilter() {
return new OpenEntityManagerInViewFilter();
}

}

image-20190816211446654


image-20190816211611231


image-20190816211653849


image-20190816211845052


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 设置所有的过滤器:有顺序map
* key = 拦截的url地址
* value = 过滤器类型
*
*/
Map<String,String> filterMap = new LinkedHashMap<>();
//filterMap.put("/user/home","anon");//当前请求地址可以匿名访问

//具有某中权限才能访问
//使用过滤器的形式配置请求地址的依赖权限
//filterMap.put("/user/home","perms[user-home]"); //不具备指定的权限,跳转到setUnauthorizedUrl地址

//使用过滤器的形式配置请求地址的依赖角色
//filterMap.put("/user/home","roles[系统管理员]");

filterMap.put("/user/**","authc");//当前请求地址必须认证之后可以访问

filterFactory.setFilterChainDefinitionMap(filterMap);

return filterFactory;
}

image-20190816213649328


Shiro中的会话管理

image-20190816213846325

image-20190816213907920


image-20190816214329905


Shiro结合redis的统一会话管理

image-20190816215523813