定制软件基于JWT实现简单的用户登陆验证(超详细)

一. 什么是

JSON Web Token(JWT)定制软件是目前最流行的定制软件跨域身份验证解决方案

二.JWT定制软件一般用于做什么

  • 授权 

定制软件即在用户登录成功以后,定制软件为用户颁发一个token(令牌),定制软件用户便可以使用这个token定制软件令牌访问后台的接口

  • 加密

使用JWT定制软件可以对接口的参数进行加密,定制软件在后台验证成功以后才定制软件能真正进行处理

三.为什么要使用JWT进行认证,而不使用session、cookie?

基于cookie的认证,存在如下问题:

  • CSRF:session基于cookie,如果cookie被截获,用户很容易收到跨站请求伪造的攻击。

基于session的认证,存在如下问题:

  • 开销大:每个用户在认证之后,都要在服务端做一次记录,以方便该用户下次请求的鉴别。通常session保存在内存中,随着认证用户的增多,服务端存储session的开销显著增大。
  • 扩展性低:用户认证记录存储在认证服务器的内存中,这意味着用户下次请求仍要访问这台服务器才能拿到授权。在分布式应用上,这限制了负载均衡的能力,进而限制了整个应用的扩展能力。

JWT是一种基于token的认证机制,它类似于HTTP协议一样是无状态的,不需要在服务端保留用户的认证信息或者会话信息。除此之外,基于token的鉴权机制不需要考虑用户在哪一台服务器登录,为应用扩展提供了便利。

       说了这么多,那我们就通过一个小案例来实战一下~

环境准备:

      一个简单的springboot工程:

      基本的user实体类:

  1. package com.example.mybatixtest.pojo;
  2. import com.baomidou.mybatisplus.annotation.IdType;
  3. import com.baomidou.mybatisplus.annotation.TableField;
  4. import com.baomidou.mybatisplus.annotation.TableId;
  5. import com.baomidou.mybatisplus.annotation.TableName;
  6. import java.io.Serializable;
  7. import com.sun.xml.internal.ws.developer.Serialization;
  8. import lombok.Data;
  9. /**
  10. *
  11. * @TableName user
  12. */
  13. @TableName(value ="user")
  14. @Data
  15. @Serialization
  16. public class User implements Serializable {
  17. /**
  18. *
  19. */
  20. private String userId;
  21. /**
  22. *
  23. */
  24. private String username;
  25. /**
  26. *
  27. */
  28. private String password;
  29. @TableField(exist = false)
  30. private static final long serialVersionUID = 1L;
  31. }

 1.我们先在pom.xml文件中导入jwt依赖

  1. <!--引入jwt-->
  2. <dependency>
  3. <groupId>com.auth0</groupId>
  4. <artifactId>java-jwt</artifactId>
  5. <version>3.9.0</version>
  6. </dependency>

2.创建一个JWT工具类,封装JWT的相关操作(加密、验证、解密)

  1. package com.canrio.onlinemusic.utils;
  2. import com.auth0.jwt.JWT;
  3. import com.auth0.jwt.JWTVerifier;
  4. import com.auth0.jwt.algorithms.Algorithm;
  5. import com.auth0.jwt.interfaces.DecodedJWT;
  6. import org.springframework.stereotype.Component;
  7. import java.util.Date;
  8. @Component
  9. public class TokenUtil {
  10. private static final long EXPIRE_TIME= 15*60*1000;
  11. private static final String TOKEN_SECRET="token123"; //密钥盐
  12. /**
  13. * 签名生成
  14. * @return
  15. */
  16. public static String sign(String name,String userId){
  17. String token = null;
  18. try {
  19. Date expiresAt = new Date(System.currentTimeMillis() + EXPIRE_TIME);
  20. token = JWT.create()
  21. .withIssuer("auth0").withClaim("id","id")
  22. .withClaim("username", name)
  23. .withClaim("userId",userId)
  24. .withExpiresAt(expiresAt)
  25. // 使用了HMAC256加密算法。
  26. .sign(Algorithm.HMAC256(TOKEN_SECRET));
  27. } catch (Exception e){
  28. e.printStackTrace();
  29. }
  30. return token;
  31. }
  32. /**
  33. * 签名验证
  34. * @param token
  35. * @return
  36. */
  37. public static boolean verify(String token){
  38. try {
  39. JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).withIssuer("auth0").build();
  40. DecodedJWT jwt = verifier.verify(token);
  41. System.out.println("认证通过:");
  42. System.out.println("issuer: " + jwt.getIssuer());
  43. System.out.println("username: " + jwt.getClaim("username").asString());
  44. System.out.println("userId: " + jwt.getClaim("userId").asString());
  45. System.out.println("id"+jwt.getClaim("id").asString());
  46. System.out.println("过期时间: " + jwt.getExpiresAt());
  47. return true;
  48. } catch (Exception e){
  49. return false;
  50. }
  51. }
  52. public static String getId(String token){
  53. JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).withIssuer("auth0").build();
  54. DecodedJWT jwt = verifier.verify(token);
  55. String id = jwt.getClaim("userId").asString();
  56. return id;
  57. }
  58. }

3.创建一个登录拦截器LoginInterceptor,用于获取请求的Token并进行验证

  1. package com.example.mybatixtest.interceptor;
  2. import com.example.mybatixtest.utils.TokenUtil;
  3. import org.springframework.http.HttpMethod;
  4. import org.springframework.stereotype.Component;
  5. import org.springframework.web.servlet.HandlerInterceptor;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. @Component //在容器中进行注册
  9. public class LoginInterceptor implements HandlerInterceptor {
  10. @Override
  11. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  12. if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
  13. System.out.println("OPTIONS请求,放行");
  14. return true;
  15. }
  16. String token = request.getHeader("token");
  17. if(TokenUtil.verify(token)){
  18. return true;
  19. }
  20. // 失败我们跳转回登录页面
  21. request.setAttribute("msg","登录出错");
  22. request.getRemoteHost();
  23. request.getRequestDispatcher("/login").forward(request,response);
  24. return false;
  25. }
  26. }

4.修改web配置,添加一个配置类WebConfig对LoginInterceptor进行注册和制定拦截规则

  1. package com.example.mybatixtest.config;
  2. import com.example.mybatixtest.interceptor.LoginInterceptor;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  6. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  7. //@Configuration 告诉springboot这是一个配置类
  8. @Configuration
  9. public class WebConfig implements WebMvcConfigurer {
  10. @Autowired
  11. LoginInterceptor loginInterceptor;
  12. @Override
  13. public void addInterceptors(InterceptorRegistry registry) {
  14. registry.addInterceptor(loginInterceptor)
  15. .addPathPatterns("/**") //默认对所有请求进行拦截
  16. .excludePathPatterns("/userLogin","/static/**"); //对login页面和静态资源不拦截
  17. }
  18. }

 5.我们编写简单的请求进行验证

  1. @GetMapping(value = "/userLogin")
  2. public String userLogin(@RequestParam("username") String username, @RequestParam("password") String password) {
  3. //创建一个条件构造器
  4. QueryWrapper<User> userQueryWrapper = new QueryWrapper<User>();
  5. //传入查询条件
  6. userQueryWrapper.eq("username", username).eq("password", password);
  7. User user = userService.getOne(userQueryWrapper);
  8. if (user != null) {
  9. String res = TokenUtil.sign(username, user.getUserid());
  10. System.out.println(res);
  11. return res;
  12. }
  13. return "失败";
  14. }

6.使用postman进行验证,我们可以发现用户端得到了一串token字符串

 7.这里再用我之前做过的小项目对token进行验证

  1. /**
  2. * 根据返回的token解析用户id并返回该用户的歌单列表
  3. *
  4. * @return
  5. */
  6. @GetMapping("/getUserFolderByUserId")
  7. public List<UserFolder> getUserFolderByUserID() {
  8. String token = request.getHeader("token");
  9. System.out.println(token);
  10. String id = TokenUtil.getId(token);
  11. QueryWrapper<UserFolder> userFolderQueryWrapper = new QueryWrapper<>();
  12. userFolderQueryWrapper.eq("user_userid", id);
  13. List<UserFolder> list = userFolderService.list(userFolderQueryWrapper);
  14. System.out.println(list);
  15. return list;
  16. }

当我们请求头不携带token时:

当我们携带时: 

 

至此,我们基于JWT实现简单的用户登陆验证教程就结束啦~

感谢您的阅读,希望我的文章能给你带来帮助!!!

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发