zoukankan      html  css  js  c++  java
  • spring boot 配置拦截器验证使用 token 登录

    1、自定义登录注解

    package io.xiongdi.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * @author wujiaxing
     * @date 2019-07-12
     * 登录校验
     */
    @Target(ElementType.METHOD)
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Login {
    }

    2、创建 token 实体类

    package io.xiongdi.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Builder;
    import lombok.Data;
    
    import java.io.Serializable;
    import java.time.LocalDateTime;
    
    /**
     * 用户token
     * @author wujiaxing
     * @date ${2019-6-30}
     */
    @Data
    @TableName("tb_token")
    @Builder
    public class TokenEntity implements Serializable {
    
        private static final long serialVersionUID = 5584132314624077161L;
    
        public TokenEntity(){}
    
        public TokenEntity(long userId, String token, LocalDateTime expireTime, LocalDateTime updateTime) {
            this.userId = userId;
            this.token = token;
            this.expireTime = expireTime;
            this.updateTime = updateTime;
        }
    
        /**
         *  用户ID
         */
        @TableId(type = IdType.INPUT)
        private long userId;
        /**
         * token
         */
        private String token;
        /**
         *  过期时间
         */
        private LocalDateTime expireTime;
        /**
         *  修改时间
         */
        private LocalDateTime updateTime;
    }

    3、创建处理 token 的接口方法

    package io.xiongdi.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import io.xiongdi.entity.TokenEntity;
    
    /**
     * token
     * @author wujiaxing
     * @date 2019-06-30
     */
    public interface TokenService extends IService<TokenEntity> {
    
        /**
         * <p>
         *     根据请求token查询token信息
         * </p>
         * @param token
         * @return
         */
        TokenEntity queryByToken(String token);
    
        /**
         *  创建token
         * @param userId 用户ID
         * @return 返回token信息
         */
        TokenEntity createToken(long userId);
    
        /**
         * 设置token过期
         * @param userId 用户ID
         */
        void expireToken(long userId);
    }
    package io.xiongdi.service.impl;
    
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import io.xiongdi.dao.TokenDao;
    import io.xiongdi.entity.TokenEntity;
    import io.xiongdi.service.TokenService;
    import org.springframework.stereotype.Service;
    
    import java.time.LocalDateTime;
    import java.time.ZoneId;
    import java.time.ZoneOffset;
    import java.util.Date;
    import java.util.UUID;
    
    /**
     * @author wujiaxing
     * @date 2019-07-08
     */
    @Service("tokenService")
    public class TokenServiceImpl extends ServiceImpl<TokenDao, TokenEntity> implements TokenService {
    
        /**
         * 12 小时过期 单位:毫秒
         */
        private final static int EXPIRE = 3600 * 12 * 1000;
    
        /**
         *  根据请求头的token查询数据库对应的token信息
         * @param token
         * @return
         */
        @Override
        public TokenEntity queryByToken(String token) {
            return this.getOne(new QueryWrapper<TokenEntity>().eq("token", token));
        }
    
        @Override
        public TokenEntity createToken(long userId) {
            // 得到当前时间
            LocalDateTime now = LocalDateTime.now();
            // 根据过期时间加上当前时间,得到token的有效期
            long indate = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli() + EXPIRE;
            LocalDateTime tokenExpireDateTime = LocalDateTime.ofInstant(new Date(indate).toInstant(), ZoneId.systemDefault());
            // 生成token
            String token = generateToken();
            // 创建实体对象
            TokenEntity tokenEntity = TokenEntity.builder().expireTime(tokenExpireDateTime).userId(userId).token(token).updateTime(now).build();
            // 放入数据库保存
            this.saveOrUpdate(tokenEntity);
            return tokenEntity;
        }
    
        /**
         *  生成token
         * @return
         */
        private String generateToken() {
            return UUID.randomUUID().toString().replace("-", "");
        }
    
        @Override
        public void expireToken(long userId) {
            // 获取当前时间
            LocalDateTime now = LocalDateTime.now();
            TokenEntity tokenEntity = TokenEntity.builder().userId(userId).expireTime(now).updateTime(now).build();
            this.saveOrUpdate(tokenEntity);
        }
    }

    4、创建拦截器

    package io.xiongdi.interceptor;
    
    import io.xiongdi.annotation.Login;
    import io.xiongdi.common.exception.XDException;
    import io.xiongdi.common.utils.ResultType;
    import io.xiongdi.entity.TokenEntity;
    import io.xiongdi.service.TokenService;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.time.LocalDateTime;
    
    /**
     * 权限(token)验证
     * @author wujiaxing
     * @date 2019-06-30
     */
    @Component
    public class AuthorizationInterceptor implements HandlerInterceptor {
    
        @Autowired
        private TokenService tokenService;
    
        public final static String USER_KEY = "userId";
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            Login annotation;
            // 如果处理对象是一个处理方法,则获取到方法上的注解
            if (handler instanceof HandlerMethod) {
                annotation = ((HandlerMethod)handler).getMethodAnnotation(Login.class);
            // 否则直接放过拦截的请求
            } else {
                return true;
            }
            // 说明此方法没有Login注解
            if (annotation == null) {
                return true;
            }
    
            // 从请求头获取token
            String token = request.getHeader("token");
    
            // 如果请求头没有token,则从请求参数中取
            if (StringUtils.isBlank(token)) {
                token = request.getParameter("token");
            }
            // 如果还是没有token,则抛异常
            if (StringUtils.isBlank(token)) {
               throw new XDException(ResultType.TOKEN_NULL);
            }
    
            // 查询token信息
            TokenEntity tokenEntity = tokenService.queryByToken(token);
    
            // 如果token信息是否为null或是否过期,则抛异常
            if (tokenEntity == null || tokenEntity.getExpireTime().isBefore(LocalDateTime.now())) {
                throw new XDException(ResultType.TOKEN_EXPIRE);
            }
    
            // 否则,存入request作用域,后续根据userId,获取用户信息
            request.setAttribute(USER_KEY, tokenEntity.getUserId());
    
            return true;
        }
    }

    5、将拦截器配置给 spring boot

    package io.xiongdi.config;
    
    import io.xiongdi.interceptor.AuthorizationInterceptor;
    import io.xiongdi.resolver.LoginUserHandlerMethodArgumentResolver;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.method.support.HandlerMethodArgumentResolver;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    import java.util.List;
    
    /**
     * @author wujiaxing
     * <p>
     *     此配置类可配置拦截器、参数解析器、返回值解析器、跨域支持等等
     * </p>
     */
    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    
        @Autowired
        private AuthorizationInterceptor authorizationInterceptor;
        @Autowired
        private LoginUserHandlerMethodArgumentResolver loginUserHandlerMethodArgumentResolver;
    
        /**
         * 拦截器配置
         * @param registry
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(authorizationInterceptor).addPathPatterns("/api/**");
        }
    
        /**
         * 跨域支持配置
         * @param registry
         */
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**").allowCredentials(true).allowedOrigins("*").allowedMethods("GET", "PUT", "DELETE", "POST", "OPTIONS").maxAge(3600);
        }
    
        /**
         * 参数解析配置
         * @param resolvers
         */
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            resolvers.add(loginUserHandlerMethodArgumentResolver);
        }
    }

    6、配置已经好了,可以开始测试

    只要你不觉得尴尬,那尴尬的就是别人
  • 相关阅读:
    LeetCode(65):有效数字
    LeetCode(64):最小路径和
    物理Data Guard的日常维护
    Oracle Data Guard的配置
    SQL基础--完整性约束
    使用rman迁移数据库到异机
    Oracle的表空间和数据文件
    Oracle的控制文件
    Oracle重做日志文件
    RMAN的恢复篇
  • 原文地址:https://www.cnblogs.com/wujiaxing/p/11180062.html
Copyright © 2011-2022 走看看