0


如何在springboot项目中使用JWT

如何在springboot项目中使用JWT

1、JWT是什么

JWT的全称为:JSON Web Token ,它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

2、JWT的组成

JWT由三部分组成,它们之间用圆点(.)连接。这三部分分别是:
Header.Payload.Signature

  1. Header是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256)。
  2. Payload是有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择。
  3. Signature是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。
  4. 完整的JWT如下:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI0NDAxODQyMDAwMDAwMDAwMDAifQ.Q7bE35fu4ZrE8FHF9HJfwAEV_pAYOtzYGDw5EVAnGts

3、JWT的用途

  1. 客户端使用用户名和密码请求登录
  2. 服务端收到请求,验证用户名和密码
  3. 验证成功后,服务端会签发一个token,再把这个token返回给客户端
  4. 客户端收到token后可以把它存储起来,比如放到cookie中
  5. 客户端每次向服务端请求资源时需要携带服务端签发的token,可以在cookie或者header中携带
  6. 服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就向客户端返回请求数据

4、为什么使用JWT

不动产预约登记系统项目开发初期,我试图用session来保存暂时需要保存或者暂时生成的数据,例如保存用户的账号密码记住用户登录的状态、保存各行政区的预约号球数量、保存用户当天取消预约次数等。但是使用session应用于前后端分离项目存在一定的弊端,例如由于session是存在与服务器的物理内存中,所以在分布式系统中,这种方式将会失效、因为session认证本质基于cookie,由于基于Cookie,而cookie无法跨域,所以session的认证也无法跨域,对单点登录不适用。所以使用session来进行开发项目并可行。
在这时了解到了JWT。结合项目场景的使用,JWT能满足项目基本功能的开发,原因有一下几点:

  1. 因为JWT Token是以JSON加密形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。
  2. 不需要在服务端保存会话信息,也就是说不依赖于cookie和session,所以没有了传统session认证的弊端,特别适用于分布式微服务。
  3. 单点登录友好:使用Session进行身份认证的话,由于cookie无法跨域,难以实现单点登录。但是,使用token进行认证的话,token可以被保存在客户端的任意位置的内存中,不一定是cookie,所以不依赖cookie,不会存在这些问题。
  4. 适合移动端应用:使用Session进行身份认证的话,需要保存一份信息在服务器端,而且这种方式会依赖到Cookie(需要 Cookie保存 SessionId),所以不适合移动端。

5、如何在项目中使用JWT

1、引入依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>

2、生成token

根据使用场景,需要将生成token的方法写到对应的类中。在开发不动产预约登记系统中,我将生成token的方法写在了用户的基类中:

public class Account {//用户信息
    String id;
    String email;
    String username;
    String password;
    int cancelnum;

    public String getToken(Account account) {
        String token="";
        token= JWT.create().withAudience(account.getId())
                .sign(Algorithm.HMAC256(account.getPassword()));
        return token;
    }
}

为了确保token的唯一性,需要将用户的id以及password写入到token中进行加密.

3、开放路径

@Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry
                .addInterceptor(authorizeInterceptor)
                .addPathPatterns("/**");// 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
    }

需要开放路径因为拦截器需要对全局路径进行扫描。

4、配置拦截器

public class AuthorizeInterceptor implements HandlerInterceptor {
    @Resource
    UserMapper mapper;

    String token;

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
        // 如果不是映射到方法直接通过
        if(!(object instanceof HandlerMethod)){
            return true;
        }
        HandlerMethod handlerMethod=(HandlerMethod)object;
        Method method=handlerMethod.getMethod();
        //检查是否有passtoken注释,有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        //检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                // 执行认证
                if (token == null) {
                    throw new RuntimeException("无token,请重新登录");
                }
                // 获取 token 中的 user id
                String email;
                try {
                    email = JWT.decode(token).getAudience().get(0);
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("401");
                }
                Account account = mapper.findAccountByEmail(email);
                if (account == null) {
                    throw new RuntimeException("用户不存在,请重新登录");
                }
                // 验证 token
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(account.getPassword())).build();
                try {
                    jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {
                    throw new RuntimeException("401");
                }
                return true;
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest,
                           HttpServletResponse httpServletResponse,
                           Object o, ModelAndView modelAndView) throws Exception {

    }
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse,
                                Object o, Exception e) throws Exception {
    }

}

5、使用

根据项目功能,需要在登录接口写入注解以进行生成token并返回。

@UserLoginToken
    @PostMapping("/login2")//登录接口
    public Object login(@Pattern(regexp = EMAIL_REGEX) @Length(min = 10, max = 20) @RequestParam("email") String email,
                                  @Length(min = 6, max = 16) @RequestParam("password")String password,HttpSession session){
        JSONObject jsonObject=new JSONObject();
        Account account=service.login(email,password);
        if(account==null){
            jsonObject.put("status",401);
            jsonObject.put("success",false);
            jsonObject.put("message","登录失败,账号或密码错误");
            return jsonObject;
        }else  {
                String token = account.getToken(account);
                jsonObject.put("status",200);
                jsonObject.put("success",true);
                jsonObject.put("Token", token);
                jsonObject.put("message", account);
                return jsonObject;
            }
    }

6、运行效果

jwt在不动产登记预约系统的使用效果

部分参考、引用:

https://blog.csdn.net/weixin_45070175/article/details/118559272

标签: spring boot java spring

本文转载自: https://blog.csdn.net/weixin_51496936/article/details/131423529
版权归原作者 .Hugh. 所有, 如有侵权,请联系我们删除。

“如何在springboot项目中使用JWT”的评论:

还没有评论