父依赖

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
<modules>
<module>security-jwt</module>
<module>web-jwt</module>
<module>shiro-jwt</module>
</modules>

<packaging>pom</packaging>

<properties>
<java.version>8</java.version>
<skipTests>true</skipTests>
<spring-boot.version>2.6.13</spring-boot.version>
<mybatis-plus.version>3.5.5</mybatis-plus.version>
<mysql.version>8.0.33</mysql.version>
<lombok.version>1.18.30</lombok.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>

</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- Hutool 全部模块 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version> <!-- 请根据需要选择版本 -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

子依赖

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
<dependencies>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.19.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

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

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

</dependencies>

配置文件yml

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
server:
port: 9002
spring:
application:
name: security
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://59.110.47.222:3306/work?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
password: 196691
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
defaultPropertyInclusion: non_null
mvc:
throw-exception-if-no-handler-found: true
web:
resources:
add-mappings: false
redis:
host: 59.110.47.222
port: 6379
database: 1
timeout: 5000
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
call-setters-on-nulls: true
shiro:
jwt:
# 加密秘钥
secret: kGgySFGcfQML4ZOvvlE7856YvSCsbjBf
# token有效时长,1天,单位毫秒
expire: 86400000
header:
# 加密算法
alg: HS256
# token类型
typ: JWT

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
83
84
85
86
package cn.zou.shiro.config;

import cn.zou.shiro.realm.CustomRealm;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;

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

@Configuration
public class ShiroConfig {

/**
*
* 自定义Realm
*/
@Bean
public CustomRealm customRealm() {
return new CustomRealm();
}

/**
* 安全管理器
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置自定义的 Realm
securityManager.setRealm(customRealm());
// 关闭 ShiroDAO 功能
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultSessionStorageEvaluator();
sessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
return securityManager;
}

/**
*
* Shiro 的过滤器
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager());

// 添加自定义自定义过滤器
Map<String, Filter> filterMap = new HashMap<>();
filterMap.put("jwt", new JwtFilter());
shiroFilterFactoryBean.setFilters(filterMap);

// 设置过滤规则
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/user/login", "anon");
filterChainDefinitionMap.put("/**", "jwt");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

return shiroFilterFactoryBean;
}

//开启shiro aop注解支持.
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}

@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}

}

CustomRealm

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

import cn.zou.shiro.config.JwtToken;
import cn.zou.shiro.config.JwtUtil;
import cn.zou.shiro.config.RedisUtil;
import cn.zou.shiro.constant.RedisConstant;
import cn.zou.shiro.entity.PermEntity;
import cn.zou.shiro.entity.RoleEntity;
import cn.zou.shiro.entity.UserEntity;
import cn.zou.shiro.mapper.UserMapper;
import cn.zou.shiro.service.UserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
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 javax.annotation.Resource;
import java.util.Map;

@Slf4j
public class CustomRealm extends AuthorizingRealm {

@Resource
private UserMapper userMapper;

@Resource
private UserService userService;
@Resource
private JwtUtil jwtUtil;
@Resource
private RedisUtil redisUtil;

// 这个方法要重写,debug源码得知shiro会判断token的类型是不是自己支持的类型,不重写的话会报错
@Override
public boolean supports(AuthenticationToken authenticationToken) {
return authenticationToken instanceof JwtToken;
}

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
log.info("进入认证中心");

// JwtToken 中重写 getPrincipal 方法
String token = authenticationToken.getPrincipal().toString();
// 验证token正确
if (!jwtUtil.verify(token)){
throw new AuthenticationException("token错误");
}
//从token解析用户名,从数据库查询用户是否存在
// Map<String, String> map = jwtUtil.parseToken(token);
// String username = map.get("username");
// LambdaQueryWrapper<UserEntity> wrapper = new LambdaQueryWrapper<UserEntity>()
// .eq(UserEntity::getUsername, username);
// UserEntity user = userMapper.selectOne(wrapper);
UserEntity user = (UserEntity) redisUtil.get(RedisConstant.PREFIX_SHIRO_JWT + token);

if(user == null){
throw new UnknownAccountException("用户在Redis不存在");
}else{
//封装成 SimplePrincipalCollection,传递给 doGetAuthorizationInfo() 方法
return new SimpleAuthenticationInfo(user, token, getName());
}
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
log.info("进入授权中心");
// 获取SimpleAuthenticationInfo传递的用户信息
UserEntity userEntity = (UserEntity) principalCollection.getPrimaryPrincipal();

// 从 UserEntity 对象中获取用户名
String name = userEntity.getUsername();
// 根据身份标识获取数据库中用户信息
UserEntity user = userService.listRolePerm(name);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

for (RoleEntity role : user.getRoles()) {
//添加角色
simpleAuthorizationInfo.addRole(role.getRoleName());
//添加权限
for (PermEntity perm : role.getPerms()) {
simpleAuthorizationInfo.addStringPermission(perm.getPermName());
}
}
System.out.println(simpleAuthorizationInfo);
return simpleAuthorizationInfo;
}
}

UserService

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.zou.shiro.service;

import cn.zou.shiro.entity.PermEntity;
import cn.zou.shiro.entity.RoleEntity;
import cn.zou.shiro.entity.UserEntity;
import cn.zou.shiro.mapper.UserMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

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

@Service
@RequiredArgsConstructor
public class UserService {

private final UserMapper userMapper;

public UserEntity listRolePerm(String username) {

//根据用户名查询用户
LambdaQueryWrapper<UserEntity> wrapper = new LambdaQueryWrapper<UserEntity>()
.eq(UserEntity::getUsername, username);
UserEntity user = userMapper.selectOne(wrapper);

//用户拥有的角色,角色拥有的权限
Set<RoleEntity> roles = userMapper.listRoleByUserName(username);
HashSet<RoleEntity> roleContain = new HashSet<>();
for (RoleEntity role : roles) {
Set<PermEntity> perm = userMapper.listPermByRoleName(role.getRoleName());
RoleEntity roleEntity = new RoleEntity();
roleEntity.setRoleName(role.getRoleName());
roleEntity.setPerms(perm);
roleContain.add(roleEntity);
}
//把用户的角色权限信息返回
user.setRoles(roleContain);
return user;
}
}

JwtFilter过滤器

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
package cn.zou.shiro.config;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;

@Slf4j
@Component
public class JwtFilter extends BasicHttpAuthenticationFilter {

/**
* 过滤器拦截请求的入口方法
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
// 判断请求头是否带上token
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader("Authorization");
// token不存在,禁止访问
if (StrUtil.isEmpty(token)) {
log.info("token不存在,禁止访问");
return false;
}
try {
// 交给自定义 Realm
SecurityUtils.getSubject().login(new JwtToken(token));
return true;
} catch (Exception e) {
log.info("token校验失败");
return false;
}
}
/**
* isAccessAllowed()方法返回false,即认证不通过时进入onAccessDenied方法
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setStatus(400);
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
HashMap<String, String> map = new HashMap<>();
map.put("code", "400");
map.put("msg", "token校验失败,禁止访问");
out.print(JSONUtil.toJsonStr(map));
out.flush();
out.close();
return false;
}
// 对跨域访问提供支持
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
// 跨域发送一个option请求
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}


}

JwtToken重写

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

import org.apache.shiro.authc.AuthenticationToken;

public class JwtToken implements AuthenticationToken {

private String token;

public JwtToken(String token) {
this.token = token;
}
@Override
public Object getPrincipal() {
return token;
}

@Override
public Object getCredentials() {
return token;
}
}

JwtUtil工具类

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
package cn.zou.shiro.config;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class JwtUtil {

@Value("${shiro.jwt.secret}")
private String secret;
@Value("${shiro.jwt.expire}")
private Long expire;
@Value("${shiro.jwt.header.alg}")
private String headerAlg;
@Value("${shiro.jwt.header.typ}")
private String headerTyp;



/**
* 生成token
*/
public String getToken(String username, long currentTimeMillis) {
// 设置秘钥
String stringBuilder = username + secret;

// 设置jwt头header
Map<String, Object> headerClaims = new HashMap<>();
headerClaims.put("alg", headerAlg); // 签名算法
headerClaims.put("typ", headerTyp); // token 类型
// 设置jwt的header,负载以及加密算法
String token = JWT
.create()
.withHeader(headerClaims)
.withClaim("username" ,username)
.withClaim("expire", currentTimeMillis + expire)
.sign(Algorithm.HMAC256(stringBuilder));
return token;
}

/**
* 无需秘钥就能获取其中的信息
* 解析token.
* {
* "account": "account",
* "timeStamp": "134143214"
* }
*/
public Map<String, String> parseToken(String token) {
HashMap<String, String> map = new HashMap<String, String>();
// 解码 JWT
DecodedJWT decodedJwt = JWT.decode(token);
Claim username = decodedJwt.getClaim("username");
Claim expire = decodedJwt.getClaim("expire");
map.put("username", username.asString());
map.put("expire", expire.asLong().toString());
return map;
}

/**
* 校验token是否正确
* @param token Token
* @return boolean 是否正确
*/
public boolean verify(String token) {
try {
DecodedJWT decodedJwt = JWT.decode(token);
Claim username = decodedJwt.getClaim("username");
Claim expire = decodedJwt.getClaim("expire");

Algorithm algorithm = Algorithm.HMAC256(username.asString() + secret);
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(token);
return true; // 验证成功
} catch (JWTVerificationException e) {
// 验证失败
return false;
}
}
}

RedisConfig序列化器

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
package cn.zou.shiro.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
@Bean(name = "redisTemplate")
public RedisTemplate<String, Object> getRedisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
// 设置序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置redisTemplate
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
RedisSerializer<?> stringSerializer = new StringRedisSerializer();
// key序列化
redisTemplate.setKeySerializer(stringSerializer);
// value序列化
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// Hash key序列化
redisTemplate.setHashKeySerializer(stringSerializer);
// Hash value序列化
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}

}

RedisUtil 工具类

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
package cn.zou.shiro.config;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {
// 使用jwt的过期时间毫秒
private final long defaultTimeout = 1*24*60*60*1000;

@Autowired
private RedisTemplate<String, Object> redisTemplate;

/**
* 是否存在指定的key
*
* @param key
* @return
*/
public boolean hasKey(String key) {
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}

/**
* 删除指定的key
*
* @param key
* @return
*/
public boolean delete(String key) {
return Boolean.TRUE.equals(redisTemplate.delete(key));
}

//- - - - - - - - - - - - - - - - - - - - - String类型 - - - - - - - - - - - - - - - - - - - -
/**
* 根据key获取值
*
* @param key 键
* @return
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}

/**
* 将值放入缓存
*
* @param key 键
* @param value 值
* @return true成功 false 失败
*/
public void set(String key, String value) {
set(key, value, defaultTimeout);
}

/**
* 将值放入缓存并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) -1为无期限
* @return true成功 false 失败
*/
public void set(String key, String value, long time) {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
redisTemplate.opsForValue().set(key, value, defaultTimeout, TimeUnit.SECONDS);
}
}

//- - - - - - - - - - - - - - - - - - - - - object类型 - - - - - - - - - - - - - - - - - - - -
/**
* 根据key读取数据
*/
public Object getObject(final String key) {
if (StringUtils.isBlank(key)) {
return null;
}
try {
return redisTemplate.opsForValue().get(key);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

/**
* 写入数据
*/
public boolean setObject(final String key, Object value) {
if (StringUtils.isBlank(key)) {
return false;
}
try {
setObject(key, value , defaultTimeout);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

public boolean setObject(final String key, Object value, long time) {
if (StringUtils.isBlank(key)) {
return false;
}
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
redisTemplate.opsForValue().set(key, value, defaultTimeout, TimeUnit.SECONDS);
}
return true;
}

}

RedisConstant

1
2
3
4
5
6
7
8
9
10
package cn.zou.shiro.constant;

public class RedisConstant {
private RedisConstant() {}

public static final String PREFIX_ACCESS_TOKEN = "access_token";

public static final String PREFIX_SHIRO_JWT = "shiro:jwt:";

}

UserController

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
package cn.zou.shiro.controller;

import cn.zou.shiro.config.JwtUtil;
import cn.zou.shiro.config.RedisUtil;
import cn.zou.shiro.constant.RedisConstant;
import cn.zou.shiro.entity.UserEntity;
import cn.zou.shiro.mapper.UserMapper;
import cn.zou.shiro.result.CommonResult;
import cn.zou.shiro.result.CommonResultEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

@Slf4j
@RestController
@RequestMapping(value="user",name = "用户")
@RequiredArgsConstructor
public class UserController {
private final UserMapper userMapper;
private final JwtUtil jwtUtil;
private final RedisUtil redisUtil;


@PostMapping(value = "test",name = "测试")
public CommonResult<CommonResultEnum> test(){

return CommonResultEnum.SUCCESS.setMsg("测试成功");
}
@PostMapping(value = "register",name = "注册")
public CommonResult<CommonResultEnum> register(@RequestBody UserEntity userEntity){
userMapper.insert(userEntity);
return CommonResultEnum.REGISTER_SUCCESS.getResult();
}

@PostMapping(value = "login",name = "登录")
public CommonResult<String> login(@RequestBody UserEntity userEntity){

LambdaQueryWrapper<UserEntity> wrapper = new LambdaQueryWrapper<UserEntity>()
.eq(UserEntity::getUsername, userEntity.getUsername());
UserEntity user = userMapper.selectOne(wrapper);
//用户不存在
if(user ==null){
return CommonResultEnum.USER_NOT_EXIST_ERROR.getResult();
}
//用户密码错误
if(!user.getPassword().equals(userEntity.getPassword())){
return CommonResultEnum.PASSWORD_ERROR.getResult();
}
//生成token
String token = jwtUtil.getToken(userEntity.getUsername(), System.currentTimeMillis());
redisUtil.setObject(RedisConstant.PREFIX_SHIRO_JWT+token,user,60*60);
return CommonResultEnum.LOGIN_SUCCESS.setData(token);
}

@RequiresRoles("admin")
@PostMapping("/admin")
public String admin() {
return "admin success!";
}

@RequiresPermissions("query")
@PostMapping("/query")
public String query() {
return "query success!";
}


@RequiresPermissions("add")
@PostMapping("/add")
public String add() {
return "add success!";
}
@RequiresPermissions("delete")
@PostMapping("/delete")
public String delete() {
return "delete success!";
}

@RequiresPermissions("update")
@PostMapping("/update")
public String update() {
return "update success!";
}



}

异常

自定义异常

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
package cn.zou.shiro.exception;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;


@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class CustomerException extends RuntimeException{
private String msg;
}
package cn.zou.shiro.exception;

import cn.zou.shiro.result.CommonResult;
import cn.zou.shiro.result.CommonResultEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;


@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

/**
* 参数校验异常捕获
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public CommonResult<Object> methodArgumentNotValidException(MethodArgumentNotValidException e) {
FieldError fieldError = e.getBindingResult().getFieldError();
if (fieldError == null) return CommonResultEnum.COMMON_ERROR_MISSING_REQUIRED_PARAMETER.getResult();
log.error("[{}]{}", fieldError.getField(), fieldError.getDefaultMessage());
return CommonResultEnum.COMMON_ERROR_MISSING_REQUIRED_PARAMETER.setMsg("[" + fieldError.getField() + "]" + fieldError.getDefaultMessage());
}
/**
* 自定义异常捕获
*/
@ExceptionHandler(CustomerException.class)
public CommonResult<Object> CustomerException(CustomerException e ) {
return CommonResultEnum.COMMON_EXCEPTION.setMsg(e.getMsg());

}
/**
* 所有错误请求方式异常捕获
*/
@ExceptionHandler(Exception.class)
public CommonResult<Object> exception(Exception e) {
log.error("未知异常", e);
return CommonResultEnum.COMMON_EXCEPTION.getResult();
}

@ExceptionHandler(AuthorizationException.class)
public CommonResult<Object> handleAuthorizationException(HttpServletRequest request, Exception ex) {

return CommonResultEnum.ACCESS_DENIED.getResult();
}
}

UserMapper

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
package cn.zou.shiro.mapper;

import cn.zou.shiro.entity.PermEntity;
import cn.zou.shiro.entity.RoleEntity;
import cn.zou.shiro.entity.UserEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select;

import java.util.Set;

public interface UserMapper extends BaseMapper<UserEntity> {

@Select("select r.role_name from my_user as u " +
"LEFT JOIN user_role as ur on u.id = ur.user_id " +
"left join my_role as r on r.id = ur.role_id " +
"where username = #{username}")
Set<RoleEntity> listRoleByUserName(String username);


@Select("select p.perm_name from my_role as r " +
"left join role_perm as rp on r.id = rp.role_id " +
"left join my_perm as p on p.id = rp.perm_id " +
"where r.role_name = #{roleName}")
Set<PermEntity> listPermByRoleName(String roleName);
}