相关基础配置工具类
pom.xml
<!--Shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.9.0</version>
</dependency>
<!-- jtw-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--mybatis-plus依赖包-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
ShiroConfig
import com.cqupt.fiter.AuthFilter;
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.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// 添加自定义过滤器
Map<String, Filter> filters=new LinkedHashMap<>();
filters.put("jwt",new AuthFilter());
shiroFilterFactoryBean.setFilters(filters);
// 配置放行的接口和拦截的接口
Map<String,String> filterMap=new LinkedHashMap<>();
filterMap.put("/swagger-ui.html", "anon"); // Swagger 2.x 的 UI 页面
filterMap.put("/swagger-ui/**", "anon"); // Swagger 3.x(OpenAPI)的 UI 页面
filterMap.put("/webjars/**", "anon"); // Swagger 静态资源(JS/CSS)
filterMap.put("/v2/api-docs", "anon"); // Swagger 2.x 的 API 文档接口
filterMap.put("/v3/api-docs", "anon"); // Swagger 3.x 的 API 文档接口
filterMap.put("/swagger-resources/**", "anon"); // Swagger 资源文件路径
filterMap.put("/doc.html", "anon"); // Knife4j(Swagger 增强 UI)的页面
filterMap.put("/doc.html/**", "anon"); // Knife4j 的静态资源
filterMap.put("/api/user/login", "anon"); //登录路径、注册路径都需要放行不进行拦截
filterMap.put("/api/user/register", "anon");
filterMap.put("/shiro/index","anon");
filterMap.put("/user/login","anon");
filterMap.put("/swagger-ui.html","anon");
filterMap.put("/doc.html","anon");
filterMap.put("/webjars/**","anon");
filterMap.put("/swagger/**","anon");
filterMap.put("/swagger-resources/**","anon");
filterMap.put("/v2/**","anon");
filterMap.put("/static/**","anon");
filterMap.put("/**","jwt");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(AuthRealm authRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(authRealm);
return defaultWebSecurityManager;
}
/**
* 开启Shiro注解,需要配置以下两个Bean
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
}
RedisConfig
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
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;
@EnableCaching
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisSerializer<Object> serializer = redisSerializer();
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(serializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public RedisSerializer<Object> redisSerializer() {
//创建JSON序列化器
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//必须设置,否则无法将JSON转化为对象,会转化成Map类型
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.registerModule(new JavaTimeModule());
serializer.setObjectMapper(objectMapper);
return serializer;
}
}
JwtUtil
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.cqupt.constant.GlobalConstant;
import com.cqupt.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Component;
import java.util.Calendar;
import java.util.Date;
@Slf4j
@Component
public class JwtUtil {
/**
* 创建token
* @param userId
* @param username
* @param currentTimeMilli 创建token的时间,校验token的时候会用到
* @return
*/
public static String createToken(Long userId, String username, String currentTimeMilli) {
Calendar nowTime = Calendar.getInstance();
nowTime.add(Calendar.MINUTE, GlobalConstant.token_expired);
Date expiresDate = nowTime.getTime();
return JWT.create().withAudience(String.valueOf(userId))
.withIssuedAt(new Date())
.withExpiresAt(expiresDate)
.withClaim(GlobalConstant.jwt_claim_username, username)
.withClaim(GlobalConstant.jwt_claim_currentTime, currentTimeMilli)
.sign(Algorithm.HMAC256(userId + GlobalConstant.jwt_secret));
}
/**
* 检验合法性
* @param token
* @return true-验证通过, false-验证失败
*/
public static boolean verifyToken(String token, Long userId) {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(userId + GlobalConstant.jwt_secret)).build();
verifier.verify(token);
return true;
}
/**
* 获取当前用户id
* @return
*/
public static Long getUserId(){
Subject subject = SecurityUtils.getSubject();
String token = subject.getPrincipal().toString();
return getAudience(token);
}
/**
* 获取JWT中的Audience,也就是userId
* @param token
* @return
*/
public static Long getAudience(String token) {
String audience;
try {
audience = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
throw new BaseException("当前Token无法解析签发者信息");
}
return Long.valueOf(audience);
}
public static String getUsername(String token) {
try {
return JWT.decode(token).getClaim(GlobalConstant.jwt_claim_username).asString();
}catch (JWTDecodeException e){
return null;
}
}
public static String getCurrentTime(String token) {
try {
return JWT.decode(token).getClaim(GlobalConstant.jwt_claim_currentTime).asString();
}catch (JWTDecodeException e){
return null;
}
}
}
SpringContextUtil
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}
/**
* 获取上下文
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 通过 bena 名称获取上下文中的 bean
*/
public static Object getBean(String name) {
return applicationContext.getBean(name);
}
/**
* 通过类型获取上下文中的bean
*/
public static Object getBean(Class<?> requiredType) {
return applicationContext.getBean(requiredType);
}
}
认证与授权
JWTToken
package com.cqupt.utils;
import org.apache.shiro.authc.AuthenticationToken;
/**
*
* 自定义的shiro接口token,可以通过这个类将string的token转型成AuthenticationToken,可供shiro使用
* 注意:需要重写getPrincipal和getCredentials方法,因为是进行三件套处理的,没有特殊配置shiro无法通过这两个
*/
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;
}
}
AuthFilter 用于在ShiroConfig中的过滤的filter
filterMap.put("/**","jwt");
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.cqupt.constant.GlobalConstant;
import com.cqupt.result.Result;
import com.cqupt.utils.JWTToken;
import com.cqupt.utils.JwtUtil;
import com.cqupt.utils.RedisUtil;
import com.cqupt.utils.SpringContextUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
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.IOException;
@Slf4j
@Component
public class AuthFilter extends BasicHttpAuthenticationFilter {
/**
* 身份认证
* @param request
* @param response
* @param mappedValue
* @return
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue){
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authToken = httpServletRequest.getHeader(GlobalConstant.header_token);
log.info("[身份认证]-请求URL: "+httpServletRequest.getRequestURI());
log.info("[身份认证]-校验token: "+authToken);
boolean result = false;
if (isLoginAttempt(request, response)) {
try {
result = executeLogin(request, response);
} catch (Exception e){
response(response, Result.error(e.getMessage()));
}
} else {
response(response, Result.error("未授权"));
}
return result;
}
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
JWTToken token = new JWTToken(this.getAuthzHeader(request));
Subject subject = this.getSubject(request, response);
subject.login(token);
onLoginSuccess(token, subject, request, response);
return true;
}
/**
* 身份认证通过
* @param token
* @param subject
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) {
String jwtToken = (String) token.getPrincipal();
try {
JwtUtil.verifyToken(jwtToken, JwtUtil.getAudience(jwtToken));
}catch (TokenExpiredException tokenExpiredException){
refreshToken(request, response);
}
return true;
}
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader(GlobalConstant.header_token);
JWTToken jwtToken = new JWTToken(token);
return jwtToken;
}
/**
* 判断用户是否登录
* @param request
* @param response
* @return
*/
@Override
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
String token = req.getHeader(GlobalConstant.header_token);
return token != null;
}
/**
* isAccessAllowed身份认证为false时调用.
* 这里重写一下,是因为看源码发现这个方法会再次执行executeLogin(),导致会再次认证失败.
* 源码中this.sendChallenge(request, response);方法有个踩坑,用浏览器请求接口的时候,如果没有Token,会先弹出登录框.
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
return false;
}
/**
* 刷新token
* @param request
* @param response
* @return
*/
public boolean refreshToken(ServletRequest request, ServletResponse response) {
RedisUtil redisUtil = (RedisUtil) SpringContextUtil.getBean(RedisUtil.class);
String token = getAuthzHeader(request);
String username = JwtUtil.getUsername(token);
Long userId = JwtUtil.getAudience(token);
// 刷新token时需要加锁解决并发问题. 注意: synchronized为单机版,如果是集群部署需要使用分布式锁
synchronized (this) {
// 如果是token正在刷新过渡期,直接放行
String oldToken = redisUtil.getTokenRefreshTransition(username);
if(oldToken != null){
if(oldToken.equals(token)){
return true;
}
}
String currentTime = String.valueOf(System.currentTimeMillis());
redisUtil.setTokenRefreshTime(username, currentTime);
token = JwtUtil.createToken(userId, username, currentTime);
JWTToken jwtToken = new JWTToken(token);
getSubject(request,response).login(jwtToken);
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Authorization", token);
httpServletResponse.setHeader("Access-Control-Expose-Headers", "Authorization");
return true;
}
}
/**
* 解决跨域问题
*/
@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"));
httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
// 前端跨域有时首先发送一个option请求,这里我们给option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}
public void response(ServletResponse response, Result result){
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("application/json;charset=UTF-8");
try {
String responseMsg = new ObjectMapper().writeValueAsString(result);
httpResponse.getWriter().append(responseMsg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
AuthRealm在这个类里重写doGetAuthorizationInfo授权方法和doGetAuthenticationInfo认证方法
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cqupt.entity.User;
import com.cqupt.exception.BaseException;
import com.cqupt.properties.JwtProperties;
import com.cqupt.service.UserService;
import com.cqupt.utils.JWTToken;
import com.cqupt.utils.JwtUtil;
import com.cqupt.utils.RedisUtil;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.Set;
/**
* @author: lhy
* 自定义Realm
*/
@Slf4j
@Component
public class AuthRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private RedisUtil redisUtil;
/**
* 限定这个realm只能处理JwtToken(不加的话会报错)
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JWTToken;
}
/**
* 授权(授权部分这里就省略了,先把重心放在认证上)
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
log.info("执行了授权");
String token = (String) principals.getPrimaryPrincipal();
String username = JwtUtil.getUsername(token);
Long userId = JwtUtil.getUserId();
//用户不存在(这个在登录时不会进入,只有在token校验时才有可能进入)
if(username == null || userId == null)
throw new UnknownAccountException();
//根据用户名,查询数据库获取到正确的用户信息
User user = userService.getOne(new QueryWrapper<User>()
.eq("login_name",username)
.eq("id",userId));
//用户不存在(这个在登录时不会进入,只有在token校验时才有可能进入)
if(user == null)
throw new UnknownAccountException("用户不存在");
//验证token有效期
if (!JwtUtil.verifyToken(token,userId)) {
throw new IncorrectCredentialsException();
}
// 从数据库或其他存储中获取用户的角色和权限
Set<String> roles = new HashSet<>();
roles.add(user.getPermission()); // 用户角色
// 构造授权信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(roles); // 设置角色
return info;
}
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) {
log.info("执行了认证");
String token = (String) auth.getCredentials(); //JwtToken中重写了这个方法了
String username = JwtUtil.getUsername(token); // 获得login name
//用户不存在(这个在登录时不会进入,只有在token校验时才有可能进入)
if(username == null)
throw new AuthenticationException("认证失败");
Object redisJwtCurrentTime = redisUtil.getTokenRefreshTime(username);
if(redisJwtCurrentTime == null){
throw new AuthenticationException("登录失效,请重新登录");
}
// 如果该token正在刷新过渡期,直接放行
String oldToken = redisUtil.getTokenRefreshTransition(username);
if(oldToken != null){
if(oldToken.equals(token)){
return new SimpleAuthenticationInfo(token, token, "AuthRealm");
}
}
// 如果token的创建时间和redis中存储的token创建时间不一致,说明该token是废弃的
// 在另一地方重复登录的时候,会出现这种情况,利用这个方法保证同一时刻只有一个token可用
String jwtCurrentTime = JwtUtil.getCurrentTime(token);
if(!jwtCurrentTime.equals(redisJwtCurrentTime.toString())){
throw new AuthenticationException("暂未登录或者登录已经失效");
}
return new SimpleAuthenticationInfo(token, token, "AuthRealm");
}
}
全局异常处理器
import com.cqupt.exception.BaseException;
import com.cqupt.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* 全局异常处理器,处理项目中抛出的业务异常
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 捕获业务异常
* @param ex
* @return
*/
@ExceptionHandler
public Result exceptionHandler(BaseException ex){
log.error("异常信息:{}", ex.getMessage());
return Result.error(ex.getMessage());
}
/**
* Shiro权限校验失败
* @return
*/
@ExceptionHandler(value = UnauthorizedException.class)
public Result handleUnauthorizedException(UnauthorizedException unauthorizedException) {
return Result.error("没有权限访问");
}
/**
* Shiro身份认证失败
* @return
*/
@ExceptionHandler(value = UnauthenticatedException.class)
public Result handleUnauthenticatedException(UnauthenticatedException unauthenticatedException) {
Throwable throwable = unauthenticatedException.getCause();
return Result.error("认证失败,重新登录");
}
}
相关全局产量
GlobalConstant
public class GlobalConstant {
public static final String header_token = "Authorization";
// jwt token中claim存放的username
public static final String jwt_claim_username = "username";
// jwt token中claim存放的token生成的时间
public static final String jwt_claim_currentTime="currentTime";
// jwt秘钥
public static String jwt_secret = "demo_secret";
// token的有效期.单位/分钟
public static final Integer token_expired = 4320;
// token的刷新有效期.单位/天
public static final Integer token_refresh_expired = 7;
// token刷新过渡期的有效期.单位/秒
public static final Integer token_refresh_transition = 10;
}
RedisConstant
public class RedisConstant {
// 存储刷新token用的前缀
public static final String shiro_refresh_token = "shiro:refresh_token";
/**
* 存储token失效的过渡期
* 作用: 避免token失效时正好遇到前端并发请求,第一个请求通过之后刷新token,第二个请求进来无法刷新token,还是会提示token无效
* 使用过渡期之后,正在过渡期的token依然能正常请求接口
*/
public static final String shiro_refresh_token_transition = "shiro_refresh_token_transition:";
}
UserController
有一些相关userService代码没有给出
@PostMapping("/login")
@ApiOperation("登录")
public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){
log.info("用户登录:{}----{}", userLoginDTO.getLoginName(),userLoginDTO.getPwd());
//登录
User user = userService.Login(userLoginDTO);//去验证自己的登录是否正确
//封装返回的VO对象
UserLoginVO userLoginVO = new UserLoginVO();
String currentTime = String.valueOf(System.currentTimeMillis());
String token = JwtUtil.createToken(user.getId().longValue(), user.getLoginName(), currentTime);
redisUtil.setTokenRefreshTime(user.getLoginName(),currentTime);
userLoginVO.setToken(token);
BeanUtils.copyProperties(user,userLoginVO);
return Result.success(userLoginVO);
}
如何开启授权
加入@RequiresRoles或者@RequiresPermissions相应注解后序访问接口时会自动调用AuthRealm中的doGetAuthorizationInfo方法中的实现
@GetMapping("/getList")
@ApiOperation("获得用户类别")
@RequiresRoles({"sup","admin"})
public Result<PageResult> getUser(UserPageQueryDTO userPageQueryDTO){
log.info("user分页查询--{}",userPageQueryDTO.toString());
PageResult result = userService.PageQuery(userPageQueryDTO);
return Result.success(result);
}
RedisUtil
import com.cqupt.constant.GlobalConstant;
import com.cqupt.constant.RedisConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* @ClassDescription: redisTemplate工具类
* 注: 同一个键存再次进行存储就是修改操作
*/
@Component
public class RedisUtil implements Serializable {
@Autowired
private RedisTemplate redisTemplate;
private static RedisTemplate redisTem;
@PostConstruct
public void initRedisTem(){
redisTem = redisTemplate;
}
/**
* 设置用户token刷新的时间
* @param username
* @param currentTime
* @return
*/
public Boolean setTokenRefreshTime(String username, String currentTime){
redisTem.opsForValue().set(RedisConstant.shiro_refresh_token+username,currentTime, GlobalConstant.token_refresh_expired, TimeUnit.DAYS);
return null;
}
/**
* 获取用户token刷新的时间
* @return
*/
public String getTokenRefreshTime(String username){
Object currentTime = redisTem.opsForValue().get(RedisConstant.shiro_refresh_token+username);
if(currentTime != null){
return currentTime.toString();
}
return null;
}
/**
* 存储正在刷新token过渡期的旧token
* @param username
* @param oldToken 旧token
* @return
*/
public Boolean setTokenRefreshTransition(String username, String oldToken){
redisTem.opsForValue().set(RedisConstant.shiro_refresh_token_transition+username,
oldToken,
GlobalConstant.token_refresh_transition,
TimeUnit.SECONDS);
return true;
}
/**
* 获取正在刷新token过渡期的旧token
* @return
*/
public String getTokenRefreshTransition(String username){
Object token = redisTem.opsForValue().get(RedisConstant.shiro_refresh_token_transition+username);
if(token != null){
return token.toString();
}
return null;
}
/**
*获取模版key的全部key
*
* @param keyTemplate
* @return
*/
public Collection getKeys(String keyTemplate){
Set keys = redisTem.keys(keyTemplate);
return keys;
}
/**
* 判断redis中是否含有该键
* @param key 键值
* @return Boolean值 false 没有此键, true 含有此键
*/
public boolean hasKey(String key){
//返回boolean值
return redisTem.hasKey(key);
}
/**
* 获取键的过期时间
* @param key 键
* @return 返回long类型的时间数值
*/
public long getExpire(String key){
return redisTem.getExpire(key);
}
/**
* 过期时间设置
* @param key 键
* @param expireMinutes 过期时间
* @return 返回设置成功
*/
public boolean setExpire(String key, long expireMinutes){
Boolean expire = redisTem.expire(key, Duration.ofMinutes(expireMinutes));
return expire;
}
public boolean setExpireByMillis(String key, long expireMillis){
Boolean expire = redisTem.expire(key, Duration.ofMillis(expireMillis));
return expire;
}
public boolean setExpireBySecond(String key, long expireSeconds){
Boolean expire = redisTem.expire(key, Duration.ofSeconds(expireSeconds));
return expire;
}
public boolean setExpireByHour(String key, long expireHours){
Boolean expire = redisTem.expire(key, Duration.ofHours(expireHours));
return expire;
}
public boolean setExpireByDay(String key, long expireDays){
Boolean expire = redisTem.expire(key, Duration.ofMinutes(expireDays));
return expire;
}
/**
* 删除键值
* @param key 键
* @return 返回删除结果
*/
public boolean delete(String key){
Boolean delete = redisTem.delete(key);
return delete;
}
/**
* 通过集合中的所有key删除对应的所有值
* @param keys 集合keys
* @return 返回boolean值
*/
public Long delete(Collection keys){
Long delete = redisTem.delete(keys);
return delete;
}
//-----------------------------对象键值存取---------------------------------------------------------------
/**
* 存值
* @param key 键
* @param value 值
*/
public void set(Object key, Object value){
redisTem.opsForValue().set(key, value);
}
/**
* 存值
* @param key 键
* @param value 值
* @param offset 位置
*/
public void set(Object key, Object value, long offset){
redisTem.opsForValue().set(key, value, offset);
}
/**
* 存值
* @param key 键
* @param value 值
* @param timeout 过期时间
*/
public void set(Object key, Object value, Duration timeout){
redisTem.opsForValue().set(key, value, timeout);
}
/**
* 存值
* @param key 键
* @param value 值
* @param timeout 过期时间
* @param timeUnit 时间单位
*/
public void set(Object key, Object value, long timeout, TimeUnit timeUnit){
redisTem.opsForValue().set(key, value, timeout, timeUnit);
}
/**
* 获取键对应的值
* @param key 键
* @return 返回键对应的值
*/
public Object get(Object key){
Object value = redisTem.opsForValue().get(key);
return value;
}
/**
* 获取键对应的值
* @param key 键
* @param start 开始位置
* @param end 结束位置
* @return 返回范围内的对应键的值
*/
public Object get(Object key, long start, long end){
Object value = redisTem.opsForValue().get(key, start, end);
return value;
}
/**
* 获取键对应的值的大小
* @param key 键
* @return 大小
*/
public long getSize(Object key){
Long size = redisTem.opsForValue().size(key);
return size;
}
//-----------------------------String键值存取---------------------------------------------------------------
/**
* 存值
* @param key 键
* @param value 值
*/
public void set(String key, String value){
redisTem.opsForValue().set(key, value);
}
/**
* 存值
* @param key 键
* @param value 值
* @param offset 位置
*/
public void setByOffset(String key, String value, long offset){
redisTem.opsForValue().set(key, value, offset);
}
/**
* 存值
* @param key 键
* @param value 值
* @param timeout 过期时间 可以使用Duration来调用相关时间参数
*/
public void set(String key, String value, Duration timeout){
redisTem.opsForValue().set(key, value, timeout);
}
/**
* 存值(时间封装)
* @param key 键
* @param value 值
* @param minutes 过期时间 分钟
*/
public void set(String key, String value, long minutes){
redisTem.opsForValue().set(key, value, Duration.ofMinutes(minutes));
}
public void setBySeconds(String key, String value, long seconds){
redisTem.opsForValue().set(key, value, Duration.ofSeconds(seconds));
}
public void setByHour(String key, String value, long hours){
redisTem.opsForValue().set(key, value, Duration.ofHours(hours));
}
public void setByDay(String key, String value, long days){
redisTem.opsForValue().set(key, value, Duration.ofDays(days));
}
/**
* 存值
* @param key 键
* @param value 值
* @param timeout 过期时间
* @param timeUnit 时间单位
*/
public void set(String key, String value, long timeout, TimeUnit timeUnit){
redisTem.opsForValue().set(key, value, timeout, timeUnit);
}
/**
* 获取键对应的值
* @param key 键
* @return 返回键对应的值
*/
public Object get(String key){
Object value = redisTem.opsForValue().get(key);
return value;
}
/**
* 获取键对应的值
* @param key 键
* @param start 开始位置
* @param end 结束位置
* @return 返回范围内的对应键的值
*/
public Object get(String key, long start, long end){
Object value = redisTem.opsForValue().get(key, start, end);
return value;
}
//-----------------------------List键值存取---------------------------------------------------------------
/**
* 根据key存储到list的指定位置
* @param key 键
* @param index list中指定索引
* @param value 值
*/
public void lSet(Object key, long index, Object value){
redisTem.opsForList().set(key, index, value);
}
/**
* 存储到列表最左侧
* @param key 键
* @param value 值
*/
public void lSet(Object key, Object value){
redisTem.opsForList().leftPush(key, value);
}
/**
* 存储到列表最左
* @param key 键
* @param pivot
* @param value 值
*/
public void lSet(Object key, Object pivot, Object value){
redisTem.opsForList().leftPush(key, pivot, value);
}
/**
* 存储到列表最右
* @param key 键
* @param value 值
*/
public void lSetR(Object key, Object value){
redisTem.opsForList().rightPush(key, value);
}
/**
* 存储到列表最右
* @param key 键
* @param pivot
* @param value 值
*/
public void lSetR(Object key, Object pivot, Object value){
redisTem.opsForList().rightPush(key, pivot, value);
}
/**
* 获取对应key的list列表大小
* @param key 键
* @return size
*/
public long lGetSize(Object key){
Long size = redisTem.opsForList().size(key);
return size;
}
/**
* 获取键对应的列表数据
* @param key 键
* @return key的值(列表)
*/
public List lGet(Object key){
List list = redisTem.opsForList().range(key, 0, -1);
return list;
}
/**
* 获取键对应的列表数据
* @param key 键
* @param start 开始位置
* @param end 结束位置
* @return 返回key对应范围内的列表数据
*/
public List lGet(Object key, long start, long end){
List list = redisTem.opsForList().range(key, start, end);
return list;
}
//-----------------------------Set(无序)键值存取---------------------------------------------------------------
/**
* 存储set类型的数据
* @param key 键
* @param values 值,可以是多个
*/
public void sSet(Object key, Object... values){
redisTem.opsForSet().add(key, values);
}
/**
* 获取key对应set类型数据的大小
* @param key 键
*/
public long sGetSize(Object key){
Long size = redisTem.opsForSet().size(key);
return size;
}
/**
* 获取set类型的数据
* @param key 键
* @return 返回一个set集合
*/
public Set sGet(Object key){
Set members = redisTem.opsForSet().members(key);
return members;
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTem.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
//-----------------------------ZSet(有序)键值存取---------------------------------------------------------------
/**
* 存储有序集合
* @param key 键
* @param value 值
* @param score 排序
*/
public void zSet(Object key, Object value, double score){
redisTem.opsForZSet().add(key, value, score);
}
/**
* 存储值
* @param key 键
* @param set 集合
*/
public void zSet(Object key, Set set){
redisTem.opsForZSet().add(key, set);
}
/**
* 获取key指定范围的值
* @param key 键
* @param start 开始位置
* @param end 结束位置
* @return 返回set
*/
public Set zGet(Object key, long start, long end){
Set set = redisTem.opsForZSet().range(key, start, end);
return set;
}
/**
* 获取key对应的所有值
* @param key 键
* @return 返回set
*/
public Set zGet(Object key){
Set set = redisTem.opsForZSet().range(key, 0, -1);
return set;
}
/**
* 获取对用数据的大小
* @param key 键
* @return 键值大小
*/
public long zGetSize(Object key){
Long size = redisTem.opsForZSet().size(key);
return size;
}
//-----------------------------HashMap键值存取---------------------------------------------------------------
/**
* 存储hashMap数据
* @param key 键
* @param hashKey map的id
* @param value 值
*/
public void hSet(Object key, Object hashKey, Object value){
redisTem.opsForHash().put(key, hashKey, value);
}
/**
* 获取大小
* @param key 键
*/
public void hGetSize(Object key){
redisTem.opsForHash().size(key);
}
/**
* 获取hashMap数据
* @param key 键
* @param hashKey map的id
* @return 返回值
*/
public Object hGet(Object key, Object hashKey){
Object o = redisTem.opsForHash().get(key, hashKey);
return o;
}
/**
* 删除数据
* @param key 键
* @param hashKeys map的id
* @return 返回Boolean
*/
public Object hDel(Object key, Object... hashKeys){
Long delete = redisTem.opsForHash().delete(key, hashKeys);
return delete;
}
}