分布式应用的登录检验解决方案 JWT+拦截器登录+放行路径开发配置

1.JWT是什么 JSON WEB TOKEN?

就是通过一定规范来生成token,然后可以通过解密算法逆向解密token,这样就可以获取用户信息

2怎么用?

JWT格式组成 头部、负载、签名
1.header+payload+signature
  头部:主要是描述签名算法
  负载:主要描述是加密对象的信息,如用户的id等,也可以加些规范里面的东西,如iss签发者,exp 过期时间,sub 面向的用户
  签名:主要是把前面两部分进行加密,防止别人拿到token进行base解密后篡改token

2.关于jwt客户端存储,这块后端接口返回后,由前端来实现
可以存储在cookie,localstorage和sessionStorage里面,后端接口返回后,前端存储

~~~~~~~~开始使用~~~~~~我是分隔线~~~~~~

3.pom.xml引入

  <!-- JWT相关 -->
    <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.7.0</version>
    </dependency>

4.代码 工具类,用来提供生成token和检验的方法

package net.wnn.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import net.wnn.model.LoginUser;

import java.util.Date;


@Slf4j
public class JWTUtil {


    /**
     * token 过期时间,正常是7天,方便测试我们改为70
     */
    private static final long EXPIRE = 1000 * 60 * 60 * 24 * 7 * 10;

    /**
     * 加密的秘钥
     */
    private static final String SECRET = "wnn.net666";

    /**
     * 令牌前缀
     */
    private static final String TOKEN_PREFIX = "wnn1024";

    /**
     * subject
     */
    private static final String SUBJECT = "wnn";


    /**
     * 根据用户信息,生成令牌
     *
     * @param loginUser
     * @return
     */
    public static String geneJsonWebToken(LoginUser loginUser) {

        if (loginUser == null) {
            throw new NullPointerException("loginUser对象为空");
        }

        String token = Jwts.builder().setSubject(SUBJECT)
                //payload
                .claim("head_img", loginUser.getHeadImg())
                .claim("id", loginUser.getId())
                .claim("name", loginUser.getName())
                .claim("mail", loginUser.getMail())
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
                .signWith(SignatureAlgorithm.HS256, SECRET).compact();

        token = TOKEN_PREFIX + token;
        return token;
    }


    /**
     * 校验token的方法
     *
     * @param token
     * @return
     */
    public static Claims checkJWT(String token) {

        try {

            final Claims claims = Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody();

            return claims;

        } catch (Exception e) {
            log.info("jwt token解密失败");
            return null;
        }

    }


}


5.登录成功后调用获取token

//登录成功,生成token TODO
LoginUser loginUser = new LoginUser();
BeanUtils.copyProperties(userDO,loginUser);

String token = JWTUtil.geneJsonWebToken(loginUser);

将获取到的token返回给前端小伙伴。

return JsonData.buildSuccess(token);

这是上面代码中,使用到的VO

package net.wnn.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;


@Data
public class LoginUser {

    /**
     * 主键
     */
    private Long id;

    /**
     * 名称
     */
    private String name;

    /**
     * 头像
     */
    @JsonProperty("head_img")
    private String headImg;

    /**
     * 邮箱
     */
    private String mail;
}


6.使用拦截器获取token 然后解密验证是否登录 ,将登录后的用户信息全局传递

package net.wnn.interceptor;

import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import net.wnn.enums.BizCodeEnum;
import net.wnn.model.LoginUser;
import net.wnn.util.CommonUtil;
import net.wnn.util.JWTUtil;
import net.wnn.util.JsonData;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

   public static ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String accessToken = request.getHeader("token");
        if(accessToken == null) {
            accessToken = request.getParameter("token");
        }

        if(StringUtils.isNotBlank(accessToken)){
        //不为空
            Claims claims = JWTUtil.checkJWT(accessToken);
            if(claims == null){
                //未登录
                CommonUtil.sendJsonMessage(response,JsonData.buildResult(BizCodeEnum.ACCOUNT_UNLOGIN));
                return false;
            }

            long userId = Long.valueOf(claims.get("id").toString());
            String headImg = (String)claims.get("head_img");
            String name = (String)claims.get("name");
            String mail = (String)claims.get("mail");


            LoginUser loginUser = new LoginUser();
            loginUser.setName(name);
            loginUser.setHeadImg(headImg);
            loginUser.setId(userId);
            loginUser.setMail(mail);

            //通过attribute传递用户信息
            //request.setAttribute("loginUser",loginUser);

           

       //通过threadLocal传递用户登录信息

            threadLocal.set(loginUser);

            return true;

        }


        CommonUtil.sendJsonMessage(response,JsonData.buildResult(BizCodeEnum.ACCOUNT_UNLOGIN));
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
 

7.配置放行路径,区分需要登录的地址和不需要登录的地址

package net.wnn.config;

import lombok.extern.slf4j.Slf4j;
import net.wnn.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
@Slf4j
public class InterceptorConfig  implements WebMvcConfigurer {



    public  LoginInterceptor loginInterceptor(){
        return new LoginInterceptor();
    }


    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(loginInterceptor())
                //拦截的路径
                .addPathPatterns("/api/user/*/**","/api/address/*/**")

                //排查不拦截的路径
                .excludePathPatterns("/api/user/*/send_code","/api/user/*/captcha",
                        "/api/user/*/register","/api/user/*/login","/api/user/*/upload");

    }
}

8.其它方法中的使用。直接通过LoginInterceptor调用就可以获得对应的用户信息

LoginUser loginUser = LoginInterceptor.threadLocal.get();

 

おすすめ

転載: blog.csdn.net/wnn654321/article/details/114240827