Shiro

Apache Shiro是一个功能强大且灵活的开源安全框架,主要功能包括用户认证、授权、会话管理以及加密。

Shiro简单灵活

image-20200921233237024

四大功能:

  • 认证:验证用户是谁,能否登陆

  • 授权:验证用户是否有权限,可以访问那些资源

  • 会话管理:即使在非Web或EJB应用程序中,也可以管理用户特定的会话

  • 加密:对数据进行加密,保证安全

其他功能支持:

  • Web支持:Shiro的Web支持API可帮助轻松保护Web应用程序。
  • 缓存:缓存是Apache Shiro API的第一层公民,可确保安全操作保持快速有效。
  • 并发性:Apache Shiro的并发功能支持多线程应用程序。
  • 测试:测试支持可以帮助您编写单元测试和集成测试,并确保您的代码将按预期进行保护。
  • “运行方式”:一种功能,允许用户采用其他用户的身份(如果允许),有时在管理方案中很有用。
  • “记住我”:在各个会话中记住用户的身份,因此他们仅在必要时登录。

Shiro组件

  • Subject:把当前用户作为一个主体,通过subject进行授权认证
  • SecurityManager:安全管理器,对全部的subject进行安全管理
  • Authenticator:认证
  • Authorizer:授权
  • Realm:领域,相当于数据源

Shiro过滤器

image-20201015123545699

SpringBoot整合Shiro

整合了授权认证、MD5盐值加密、thymeleaf、EhCache、Redis

Yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spring:
application:
name: springboot-shiro
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/shiro?useSSL=true
username: root
password: root
redis:
port: 6379
host:
password: '070409'
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:/mapper/*
type-aliases-package: cn.this52.springbootshiro.pojo

ShiroConfig

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
package cn.this52.springbootshiro.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import cn.this52.springbootshiro.realm.UserRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

@Configuration
public class ShiroConfig {// Shiro配置

@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiro = new ShiroFilterFactoryBean();
shiro.setSecurityManager(securityManager);
// 权限设置
Map<String, String> auth = new LinkedHashMap<>();
auth.put("/main", "authc"); // main请求需要认证
auth.put("/manager", "perms[manager]"); // manager请求需要有manager权限
auth.put("/admin", "roles[admin]"); // admin请求需要有admin角色

// login页面
shiro.setLoginUrl("/login");
// 未授权页面
shiro.setUnauthorizedUrl("/unAuth");

shiro.setFilterChainDefinitionMap(auth);
return shiro;
}

/**
* 安全管理器
*
* @param userRealm realm
* @return DefaultWebSecurityManager
*/
@Bean
public DefaultWebSecurityManager securityManager(UserRealm userRealm) {
return new DefaultWebSecurityManager(userRealm);

}

/**
* 注入Realm,配置MD5加密
*
* @return UserRealm
*/
@Bean
public UserRealm userRealm() {
UserRealm userRealm = new UserRealm();
// 修改凭证匹配器并设置加密算法
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher("md5");
// 设置散列次数
hashedCredentialsMatcher.setHashIterations(1024);
userRealm.setCredentialsMatcher(hashedCredentialsMatcher);

//开启缓存管理(默认是EhCache)
userRealm.setCacheManager(new EhCacheManager());
userRealm.setCachingEnabled(true);// 开启全局缓存
userRealm.setAuthenticationCachingEnabled(true);// 开启认证缓存
userRealm.setAuthorizationCachingEnabled(true);// 开启授权缓存
userRealm.setAuthenticationCacheName("authentication");// 设置认证缓存名字
userRealm.setAuthorizationCacheName("authorization");// 设置授权缓存名字
return userRealm;
}

/**
* 整合thymeleaf配置shiro方言
*
* @return shiroDialect
*/
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}

Controller

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
package cn.this52.springbootshiro.controller;

import cn.this52.springbootshiro.pojo.User;
import cn.this52.springbootshiro.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
public class UserController {
@Autowired
private UserService userService;

// 跳转到所有请求的页面,页面不存在时会报错
@GetMapping("/{url}")
public String forward(@PathVariable String url) {
return url;
}

@PostMapping("/login")
public String login(User user, Model model) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
subject.login(token);
subject.getSession().setAttribute("user", subject.getPrincipal());
return "redirect:/index";
} catch (UnknownAccountException e) {
e.printStackTrace();
// 用户不存在
model.addAttribute("msg", "用户不存在");
return "login";
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
// 密码错误
model.addAttribute("msg", "密码错误");
return "login";
}
}

@GetMapping("/unAuth")
@ResponseBody
public String unAuth() {
return "未授权无法访问!";
}

@GetMapping("/logout")
public String logout() {
SecurityUtils.getSubject().logout();
return "login";
}

@PostMapping("/register")
public String register(User user, Model model) {
int register = userService.register(user);
model.addAttribute("msg", register == 1 ? "注册成功!" : "注册失败!");
return register == 1 ? "login" : "register";

}
}

Mapper

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
package cn.this52.springbootshiro.mapper;

import cn.this52.springbootshiro.pojo.Permission;
import cn.this52.springbootshiro.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface UserMapper extends BaseMapper<User> {
@ResultMap("userMap")
@Select("select uid,username,rid,r_name\n" +
"from user\n" +
" left join user_role ur on uid = ur.user_id\n" +
" left join role on ur.role_id = role.rid where uid =#{uid}")
User findRolesByUid(int uid);


@Select("select p_name\n" +
"from role\n" +
" left join role_perm rp on role.rid = rp.role_id\n" +
" left join permission p on rp.perm_id = p.pid\n" +
"where rid = 1;")
List<Permission> findPermsByRid(int rid);
}

Pojo

1
2
3
4
5
6
7
8
9
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class Permission implements Serializable {
private static final long serialVersionUID = -5891141512288426298L;
private Integer pid;
private String pName;
}
1
2
3
4
5
6
7
8
9
10
11
12
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class Role implements Serializable {
private static final long serialVersionUID = -4021452759620212876L;
private Integer rid;
private String rName;

@TableField(exist = false)
List<Permission> permissions;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {

private static final long serialVersionUID = -265042359335069131L;
private Integer uid;
private String username;
public String password;
private String salt;

@TableField(exist = false)
private List<Role> roles;
}

Realm

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
package cn.this52.springbootshiro.shiro.realm;

import cn.this52.springbootshiro.pojo.Permission;
import cn.this52.springbootshiro.pojo.Role;
import cn.this52.springbootshiro.pojo.User;
import cn.this52.springbootshiro.service.UserService;
import cn.this52.springbootshiro.shiro.salt.MyByteSource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;

/**
* 授权
*
* @param principalCollection 用户信息
* @return 授权结果
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 获取当前用户登录信息
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getPrincipal();

// 用户对应角色权限信息
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
User rolesByUid = userService.findRolesByUid(user.getUid());
List<Role> roles = rolesByUid.getRoles();

if (!CollectionUtils.isEmpty(roles)) {
roles.forEach(role -> {
// 添加角色
authorizationInfo.addRole(role.getRName());

// 添加权限
authorizationInfo.addStringPermissions(
userService.findPermsByRid(role.getRid())
.stream()
.map(Permission::getPName)
.collect(Collectors.toList()));
});
}
return authorizationInfo;
}

/**
* 认证
*
* @param authenticationToken 认证令牌
* @return 认证结果
* @throws AuthenticationException 身份验证异常
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("=>> 认证");
// 在token中获取用户名
String username = (String) authenticationToken.getPrincipal();
// 用户令牌(包含用户信息)
// UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
User user = userService.findUser(username);
// null代表用户不存在
if (user == null) return null;

/*
* SimpleAuthenticationInfo
* Object principal 用户(可以给用户名最好给对象)
* Object credentials 密码
* ByteSource credentialsSalt 盐值
* String realmName 当前realm名
* getName() 代表当前Realm
*/
return new SimpleAuthenticationInfo(
user,
user.getPassword(),
new MyByteSource(user.getSalt()),
getName());
}
}

Service

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
package cn.this52.springbootshiro.service;

import cn.this52.springbootshiro.pojo.Permission;
import cn.this52.springbootshiro.pojo.User;

import java.util.List;

public interface UserService {
/**
* 根据用户名查找用户
*
* @param username 用户名
* @return 用户
*/
User findUser(String username);

/**
* 根据用户id查找角色
*
* @param uid 用户id
* @return 用户
*/
User findRolesByUid(int uid);

/**
* 根据角色id查找角色权限
*
* @param rid 角色id
* @return 权限列表
*/
List<Permission> findPermsByRid(int rid);

/**
* 注册
*
* @param user 用户实体
* @return 注册结果
*/
int register(User user);

}
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
package cn.this52.springbootshiro.service;

import cn.hutool.core.util.RandomUtil;
import cn.this52.springbootshiro.mapper.UserMapper;
import cn.this52.springbootshiro.pojo.Permission;
import cn.this52.springbootshiro.pojo.User;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;

@Service
@Transactional
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;

@Override
public User findUser(String username) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username", username);
return userMapper.selectOne(wrapper);
}

@Override
public User findRolesByUid(int uid) {
return userMapper.findRolesByUid(uid);
}

@Override
public List<Permission> findPermsByRid(int rid) {
return userMapper.findPermsByRid(rid);
}


@Override
public int register(User user) {
if (findUser(user.getUsername()) != null) {
return 0;
}
String salt = RandomUtil.randomString(10);
user.setPassword(new Md5Hash(user.getPassword(), salt, 1024).toHex());
user.setSalt(salt);

return userMapper.insert(user);
}

}

CustomRedisTemplate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package cn.this52.springbootshiro.redis;

import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;

@Component
public class CustomRedisTemplate {

@Bean
public static RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);

redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}

RedisCache

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
package cn.this52.springbootshiro.shiro.cache;

import cn.this52.springbootshiro.utils.ApplicationContextUtil;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Collection;
import java.util.Set;

/**
* 自定义缓存
*
* @param <K>
* @param <V>
*/
@SuppressWarnings("all") //压制警告
public class RedisCache<K, V> implements Cache<K, V> {
private String cacheName;

public RedisCache() {
}

public RedisCache(String cacheName) {
this.cacheName = cacheName;
}

@Override
public V get(K k) throws CacheException {
System.out.println("get k=>>>>>>>>>>>>>>>>>>>>" + k);

Object o = redisTemplate().opsForHash().get(cacheName, k.toString());
System.out.println("get v=>>>>>>>>>>>>>>>>>>>>" + o);
return (V) o;
}

@Override
public V put(K k, V v) throws CacheException {
System.out.println("put k=>>>>>>>>>>>>>>>>>>>>" + k);
System.out.println("put v=>>>>>>>>>>>>>>>>>>>>" + v);
redisTemplate().opsForHash().put(cacheName, k.toString(), v);
return null;
}

/**
* 注销后是否清除授权缓存
* @param k 用户信息
* @return
* @throws CacheException
*/
@Override
public V remove(K k) throws CacheException {
// return (V) redisTemplate().opsForHash().delete(cacheName, k.toString());
return null;
}

@Override
public void clear() throws CacheException {
redisTemplate().delete(cacheName);
}

@Override
public int size() {
return redisTemplate().opsForHash().size(cacheName).intValue();

}

@Override
public Set<K> keys() {
return (Set<K>) redisTemplate().opsForHash().keys(cacheName);
}

@Override
public Collection<V> values() {
return (Collection<V>) redisTemplate().opsForHash().values(cacheName);
}

public RedisTemplate<String, Object> redisTemplate() {
return (RedisTemplate<String, Object>) ApplicationContextUtil.getBean("redisTemplate");
}
}

RedisCacheManger

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package cn.this52.springbootshiro.shiro.cache;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;

/**
* 自定义缓存管理
*/
public class RedisCacheManger implements CacheManager {
@Override
public <K, V> Cache<K, V> getCache(String cacheName) throws CacheException {
return new RedisCache<K, V>(cacheName);
}
}

MybyteSource

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
package cn.this52.springbootshiro.shiro.salt;

import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.codec.Hex;
import org.apache.shiro.util.ByteSource;

import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Arrays;

public class MyByteSource implements ByteSource, Serializable {
private static final long serialVersionUID = -256381558320083357L;


private byte[] bytes;
private String cachedHex;
private String cachedBase64;

public MyByteSource() {
}

public MyByteSource(byte[] bytes) {
this.bytes = bytes;
}

public MyByteSource(char[] chars) {
this.bytes = CodecSupport.toBytes(chars);
}

public MyByteSource(String string) {
this.bytes = CodecSupport.toBytes(string);
}

public MyByteSource(ByteSource source) {
this.bytes = source.getBytes();
}

public MyByteSource(File file) {
this.bytes = (new MyByteSource.BytesHelper()).getBytes(file);
}

public MyByteSource(InputStream stream) {
this.bytes = (new MyByteSource.BytesHelper()).getBytes(stream);
}

public static boolean isCompatible(Object o) {
return o instanceof byte[] || o instanceof char[] || o instanceof String || o instanceof ByteSource || o instanceof File || o instanceof InputStream;
}

public byte[] getBytes() {
return this.bytes;
}

public boolean isEmpty() {
return this.bytes == null || this.bytes.length == 0;
}

public String toHex() {
if (this.cachedHex == null) {
this.cachedHex = Hex.encodeToString(this.getBytes());
}

return this.cachedHex;
}

public String toBase64() {
if (this.cachedBase64 == null) {
this.cachedBase64 = Base64.encodeToString(this.getBytes());
}

return this.cachedBase64;
}

public String toString() {
return this.toBase64();
}

public int hashCode() {
return this.bytes != null && this.bytes.length != 0 ? Arrays.hashCode(this.bytes) : 0;
}

public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof ByteSource) {
ByteSource bs = (ByteSource) o;
return Arrays.equals(this.getBytes(), bs.getBytes());
} else {
return false;
}
}

private static final class BytesHelper extends CodecSupport {
private BytesHelper() {
}

public byte[] getBytes(File file) {
return this.toBytes(file);
}

public byte[] getBytes(InputStream stream) {
return this.toBytes(stream);
}
}

}

Utils

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
package cn.this52.springbootshiro.utils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext context;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}


/**
* 根据bean名从ioc容器中取对象
* @param beanName bean名
* @return 实例
*/
public static Object getBean(String beanName) {
return context.getBean(beanName);
}

/**
* 根据类的class获取对象实例
* @param clazz 类class
* @param <T> 返回类型
* @return 实例
*/
public static <T> T getBean(Class<T> clazz) {
return context.getBean(clazz);
}
}

Pom依赖

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
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.4.0</version>
</dependency>

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.0</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>

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

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.5.3</version>
</dependency>

<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

错误

image-20200921232319986

配置缓存后使用devtools热部署会重复创建CacheManager,此时的CacheManager是单例的。

2.配置Shiro缓存后,Realm里面的盐值反序列化失败

image-20200922172159675

image-20200922172041041

原因:

image-20201015124722877

image-20200922172434154

  • 没有实现序列化接口

解决方案:

  • 取消authenticationCache,就不会序列化盐值,自然不会反序列化

  • 自定义ByteSource实现类,可以继承SimpleByteSource,然后实现序列化接口

    然后会在序列化的时候没问题了,但是反序列化的时候会发现一个新的错误

    image-20200922173232724

    那是因为SimpleByteSource没有无参构造,子类也无法写无参构造‘,就会抛出异常

解决方案:

复制SimpleByteSource所有信息到自定以ByteSource中,并实现ByteSource接口以及序列化接口,还得生成无参构造。

评论