springboot 集成shrio + jwt实现前后端分离认证和授权

 

相关基础配置工具类

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;
    }


}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值