Springboot JTW的实现

175 阅读2分钟

1、引入pom

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2、写一个JWTUtils

public class JWTUtils {

    //秘钥
    private static final String jwtToken = "19971112miniipo!@#$%^&*";

    public static String createToken(Long userId){
        Map<String,Object> claims = new HashMap<>();
        claims.put("userId",userId);
        JwtBuilder jwtBuilder = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256,jwtToken)
                .setClaims(claims)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 60 * 1000));
        String token = jwtBuilder.compact();
        return token;
    }

    public static Map<String,Object> checkToken(String token){
        try {
            Jwt parse = Jwts.parser().setSigningKey(jwtToken).parse(token);
            return (Map<String, Object>) parse.getBody();
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

}

3、登录的时候调用工具类生成token

@Autowired
private RedisTemplate<String,String> redisTemplate;

@Override
public ResponseResult login(LoginDto loginDto) {
    if (StringUtils.isEmpty(loginDto.getAccount()) || StringUtils.isEmpty(loginDto.getPassword())){
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE.getCode(),"请输入账号或密码");
    }
    String account = loginDto.getAccount();
    String password = loginDto.getPassword();
    //密码盐加密
    String pwd = DigestUtils.md2Hex(password + slat);
    ResponseResult responseResult = userService.findUser(account, pwd);
    User user = (User) responseResult.getData();
    if (StringUtils.isEmpty(user)){
        return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST.getCode(),"用户名或密码不存在");
    }
    //前面是最简单的登录逻辑,基本可以忽略
    //登陆成功 使用jwt生成token 返回token和存储到redis中
    
    
    //这里是使用JWTUtils的创建token 
    String token = JWTUtils.createToken(user.getId());
    //存到redis nosql数据库 顺带保存用户信息 user变成json格式的存到redis中
    redisTemplate.opsForValue().set("TOKEN_" + token, JSON.toJSONString(user),1, TimeUnit.DAYS);
    //给他返回回去 等于你登录后你就拿到一个token
    return ResponseResult.okResult(token);
}

番外内容、拿到token怎么使用? 以下是我的使用过程。我用来做登录验证。后台有一个拦截器,只开放login和register和swaager给请求头没有携带token的请求访问、其他的接口都需要请求头携带token。 简单来说就是除了login和register可以直接访问,其他的请求接口都必须在请求头带token,拿到token后去redis数据库查询。因为上面代码我在redis数据库中存有用户信息,便可以获取用户信息

@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private ILoginService loginService;

    /**
     在执行controller方法(Handler)之前进行执行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
        if (!(handler instanceof HandlerMethod)){
            return true;
        }
        
        String token = request.getHeader("Authorization");
        log.info("=================request start===========================");
        String requestURI = request.getRequestURI();
        log.info("request uri:{}",requestURI);
        log.info("request method:{}",request.getMethod());
        log.info("token:{}",token);
        log.info("=================request end===========================");

        if (token == null){
            //ResponseResult 就是一个同一返回值,如果没有检测到token就直接返回未登录
            ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN.getCode(), "未登录");
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().println(JSON.toJSONString(result));
            return false;
        }
        //如果token不为空,登录验证
        User user = loginService.checkToken(token);
        if (StringUtils.isEmpty(user)){
            //ResponseResult 就是一个同一返回值,如果没有检测到token就直接返回未登录
            ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN.getCode(), "未登录");
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().print(JSON.toJSONString(result));
            return false;
        }
        //token存在的话获取放行后的信息 用threadlocal存起来用户信息
        UserThreadLocal.put(user);
        return true;
    }

    /**
     表示拦截器全部执行完毕后
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //不删除会内存泄露
        UserThreadLocal.remove();
    }
}

threadlocal代码

public class UserThreadLocal {

    private UserThreadLocal(){

    }

    private static final ThreadLocal<User> LOCAL = new ThreadLocal<>();

    /**
     * 放
     * @param user
     */
    public static void put(User user){
        LOCAL.set(user);
    }

    /**
     * 拿
     * @return
     */
    public static User get(){
        return LOCAL.get();
    }

    /**
     * 删除
     */
    public static void remove(){
        LOCAL.remove();
    }
}

拦截器代码 只有swagger、login、register可以在请求头不携带token访问

@Configuration
public class WebMVCConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;


    @Override
    public void addCorsMappings(CorsRegistry registry) {
        //跨域配置,不可设置为*,不安全, 前后端分离项目,可能域名不一致
        //本地测试 端口不一致 也算跨域
        registry.addMapping("/**").allowedOrigins("http://localhost:9002");
    }


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
        .addPathPatterns("/**")
                .excludePathPatterns("/swagger-resources")
                .excludePathPatterns("/api/v1/login")
                .excludePathPatterns("/api/v1/register");
    }
}

还可以使用ThreadLocal获取用户信息、后台管理系统增删改查人id等