上文说到 Spring Boot 上 MongoDB 的安装配置与使用,本来打算和 Redis 合并成一篇文章的,奈何一写就写多了,只好为 Redis 单独写一篇文章。
Redis
REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。
Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
Redis 的性能非常高,速度也非常快。本文使用 Redis 存储用户的 Token。利用 Redis 的高速访问,快速鉴权。
首先,当然是导入Maven依赖,引用pom包:
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
application.yml 中配置redis:
spring:
redis:
host: 127.0.0.1
password: redispassword
port: 6379
如果对连接池有特殊需求可以继续细化配置 pool: xxx
在这里,我们手动写一个工具类 RedisUtil.java,方便使用
@Component
public class RedisUtil {
private final StringRedisTemplate redisTemplate;
/**
* 一周有多少秒
*/
private static final long WEEK_SECONDS = 7 * 24 * 60 * 60;
@Autowired
public RedisUtil(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 将 key,value 存放到redis数据库中,默认设置过期时间为一周
*
* @param key
* @param value
*/
public void set(String key, String prefix, Object value) {
redisTemplate.opsForValue().set(mergeKey(key, prefix), JSONObject.toJSONString(value), WEEK_SECONDS, TimeUnit.SECONDS);
}
/**
* 将 key,value 存放到redis数据库中,设置过期时间单位是秒
*
* @param key
* @param value
* @param expireTime
*/
public void set(String key, String prefix, Object value, long expireTime) {
redisTemplate.opsForValue().set(mergeKey(key, prefix), JSONObject.toJSONString(value), expireTime, TimeUnit.SECONDS);
}
/**
* 将 key,value 存放到redis数据库中,设置过期时间单位是秒
*
* @param key
* @param value
* @param expireTime
*/
public void set(String key, String prefix, String value, long expireTime) {
redisTemplate.opsForValue().set(mergeKey(key, prefix), value, expireTime, TimeUnit.SECONDS);
}
/**
* 判断 key 是否在 redis 数据库中
*
* @param key
* @return
*/
public boolean exists(final String key, String prefix) {
return redisTemplate.hasKey(mergeKey(key, prefix));
}
/**
* 获取与 key 对应的对象
*
* @param key
* @param clazz 目标对象类型
* @param <T>
* @return
*/
public <T> T get(String key, String prefix, Class<T> clazz) {
String s = get(key, prefix);
if (s == null) {
return null;
}
return JSONObject.parseObject(s, clazz);
}
/**
* 获取 key 对应的字符串
*
* @param key
* @return
*/
public String get(String key, String prefix) {
return redisTemplate.opsForValue().get(mergeKey(key, prefix));
}
/**
* 删除 key 对应的 value
*
* @param key
*/
public void delete(String key, String prefix) {
redisTemplate.delete(mergeKey(key, prefix));
}
/**
* merge Key
*/
private String mergeKey(String key, String prefix) {
if (Strings.isNullOrEmpty(prefix)) return key;
return prefix + ":" + key;
}
}
登陆时,用户账号和密码校验成功后,存储用户的Token:
String token = UUID.randomUUID().toString().replaceAll("-", "");
// Redis : DB.Sys -> Id:No:RoleId
redisUtil.set(token, RedisPrefixKeyEnum.Sys.toString(), emp.getId() + ":" + emp.getNo() + ":" + emp.getRoleId(), expireTime);
利用Java随机的 UUID 做为用户的 Token,并用作 redis 的 key 存进 redis 中,value 是(用户ID:用户编号:权限ID)格式。打开 redis-desktop-manager 看到信息已经保存成功:
当用户再次操作时,就可以从 HttpServletRequest 请求的头部 获取 Token ,并 Redis 查询实现鉴权了,鉴权部分如下:
@Around(value = "doAuthorize(authorize)", argNames = "pjp,authorize")
public Object deBefore(ProceedingJoinPoint pjp, Authorize authorize) throws Throwable {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 获取token
String token = request.getHeader("token");
// 获取注解
String[] codes = authorize.value();
// 未登录
if (Strings.isNullOrEmpty(token) || !redisUtil.exists(token, RedisPrefixKeyEnum.Sys.toString()))
return ApiRes.unAuthorized();
if (codes.length == 0) {
// 注解为空,登陆即可
return pjp.proceed();
} else {
String[] arr = redisUtil.get(token, RedisPrefixKeyEnum.Sys.toString()).split(":");
int roleId = Integer.valueOf(arr[2]);
// roleId 的所有权限
List<RolePermission> powers = rolePermissionMapper.selectList(
new QueryWrapper<RolePermission>().lambda()
.eq(RolePermission::getRoleId, roleId)
);
for (String code : codes) {
for (RolePermission power : powers) {
if (code.equals(power.getPermissionCode())) {
// 身份匹配成功
return pjp.proceed();
}
}
}
return ApiRes.unAuthorized("权限不足:" + Arrays.toString(codes));
}
}
(这里使用了 Spring Aop(aspect) 自定义注解权限控制,AOP相关内容会在将来的文章中会讲到)