SpringBoot 整合 JWT(基于jwt)
SpringBoot 整合 JWT (基于jwt)
0、环境搭建:
-
新建数据库表
SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0;-- ------------------------------ Table structure for user-- ----------------------------DROP TABLE IF EXISTS `user`;CREATE TABLE `user` ( `user_id` int(11) NOT NULL, `user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `password` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `real_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `phone` char(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `email` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `gender` enum('0','1') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `create_time` datetime NULL DEFAULT NULL, `modify_time` datetime NULL DEFAULT NULL, `enable` tinyint(1) UNSIGNED ZEROFILL NULL DEFAULT NULL, PRIMARY KEY (`user_id`) USING BTREE) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;-- ------------------------------ Records of user-- ----------------------------INSERT INTO `user` VALUES (1, 'root', '123456', '吴禹凡', '18358659682', '341419199@qq.xom', '0', '2021-09-20 23:20:07', NULL, 1);SET FOREIGN_KEY_CHECKS = 1;
-
导入依赖:
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.18.2</version></dependency><dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.6</version></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId></dependency><dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3.3</version></dependency><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional></dependency>
-
配置 application.yml 配置文件
server: port: 8080spring: datasource: username: root password: root url: jdbc:mysql://localhost:3306/mybatis?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSourcelogging: level: root: info cn.edu.hziee: debugmybatis-plus: configuration: mapUnderscoreToCamelCase: true type-aliases-package: cn.edu.hziee.pojo jwt: # JWT 存储的请求头 tokenHeader: Authorization # JWT 加密解密使用的密钥 secretKey: "!QAzxZ@12^3#asd.$01.0.." # JWT 的过期时长(60*60*24*7) expiration: 2235200 # 负载开头 tokenHead: Bearer
-
在 pojo 包下新建实体类
LoginUser:用户验证登录用的实体类
package cn.edu.hziee.pojo;import com.baomidou.mybatisplus.annotation.TableName;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import org.springframework.beans.factory.annotation.Autowired;@Data@NoArgsConstructor@AllArgsConstructor@TableName("user")public class UserLogin { private Integer id; private String name; private String pwd;}
-
在 mapper 包下新建 UserMapper 接口
package cn.edu.hziee.mapper;import cn.edu.hziee.bean.UserLogin;import cn.edu.hziee.pojo.User;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.baomidou.mybatisplus.core.metadata.IPage;public interface UserMapper extends BaseMapper<UserLogin> {}
-
在 service 包下新建 UserService
package cn.edu.hziee.service;import cn.edu.hziee.bean.UserLogin;import cn.edu.hziee.pojo.User;import com.baomidou.mybatisplus.extension.service.IService;import java.util.List;public interface UserService extends IService<UserLogin> { List<User> getUserByGender(String gender, int current, int sine); UserLogin login(String name, String pwd);}
-
UserServiceImpl
package cn.edu.hziee.service.impl;import cn.edu.hziee.bean.UserLogin;import cn.edu.hziee.mapper.UserMapper;import cn.edu.hziee.pojo.User;import cn.edu.hziee.service.UserService;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.core.metadata.IPage;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class UserServiceImpl extends ServiceImpl<UserMapper, UserLogin> implements UserService { @Autowired UserMapper userMapper; @Override public UserLogin login(String name, String pwd) { QueryWrapper<UserLogin> wrapper = new QueryWrapper(); wrapper.eq("name",name); UserLogin user = userMapper.selectOne(wrapper); if (user != null){ return user; } throw new RuntimeException("登陆失败~~~"); }}
-
UserController
package cn.edu.hziee.controller;import cn.edu.hziee.bean.UserLogin;import cn.edu.hziee.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap;import java.util.Map;@Controller@RequestMapping("/user")public class UserController { @Autowired private UserService userService; @ResponseBody @PostMapping("/login") public Map<String, Object> login(String name, String pwd) { Map<String, Object> map = new HashMap<>(); try { UserLogin userLogin = userService.login(name, pwd); map.put("state", true); map.put("msg", "认证成功"); } catch (Exception e) { map.put("state", false); map.put("msg", e.getMessage()); } return map; }}
-
启动类
package cn.edu.hziee;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@MapperScan("cn.edu.hziee.mapper")public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class,args); }}
-
使用 Postman 进行环境的测试
{ "msg": "认证成功", "state": true}
7.1、生成 token 令牌
LoginController
package cn.edu.hziee.controller;import cn.edu.hziee.bean.UserLogin;import cn.edu.hziee.config.JWTUtil;import cn.edu.hziee.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap;import java.util.Map;@Controller@RequestMapping("/user")public class UserController { @Autowired private UserService userService; @Autowired private JWTUtil jwtUtil; @ResponseBody @PostMapping("/login") public Map<String, Object> login(String name, String pwd) { Map<String, Object> map = new HashMap<>(); try { UserLogin userLogin = userService.login(name, pwd); Map<String,Object> payload = new HashMap<>(); payload.put("id",userLogin.getId()); payload.put("name",userLogin.getName()); String token = jwtUtil.generateToken(payload); map.put("token",token); map.put("state", true); map.put("msg", "认证成功"); } catch (Exception e) { System.out.println("11111"); map.put("state", false); map.put("msg", e.getMessage()); } return map; }}
用接口 Postman 进行接口测试
{ "msg": "认证成功", "state": true, "token": "eyJUeXBlIjoiSnd0IiwidHlwIjoiSldUIiwiYWxnIjoiSFMyNTYifQ.eyJjcmVhdGVUaW1lIjoxNjMzMTU4ODE0LCJuYW1lIjoi5p2O5ZubIiwiaWQiOjIsImV4cCI6MTYzNTM5NDAxNH0.baFHLoiQluXLk1Zpy3STSEK0uVkwkj2T-RoqOdKARPc"}
7.2、验证 token 令牌
@ResponseBody@RequestMapping("tomain")public Map<String, Object> toMain(String token){ Map<String, Object> map = new HashMap<>(); try{ DecodedJWT verify = jwtUtil.verify(token); map.put("state", true); map.put("msg", "请求成功"); return map; } catch (InvalidClaimException e){ map.put("msg", "失效的payload异常"); e.printStackTrace(); } catch (JWTDecodeException e){ map.put("msg", "JWT解码异常"); e.printStackTrace(); }catch (SignatureVerificationException e){ map.put("msg", "签名不一致"); e.printStackTrace(); }catch (TokenExpiredException e){ map.put("msg", "令牌过期异常"); e.printStackTrace(); }catch (AlgorithmMismatchException e){ map.put("msg", "算法不匹配异常"); e.printStackTrace(); }catch (Exception e){ map.put("msg", "token无效"); e.printStackTrace(); } map.put("state", false); return map;}
用接口 Postman 进行接口测试
{ "msg": "请求成功", "state": true}
在每个需要 token 验证的接口加上以上代码,会造成大量的代码冗余
7.3、JWT 拦截器
-
在 Interceptors 包下新建一个 InterceptorProperties 类(用于存储 application.yml 中的配置参数)
package cn.edu.hziee.Interceptors;import lombok.Data;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Component@Datapublic class InterceptorProperties { @Value("${jwt.secretKey}") private String secretKey; @Value("${jwt.expiration}") private Long expiration;}
-
JWTInterceptor
package cn.edu.hziee.Interceptors;import cn.edu.hziee.bean.UserLogin;import cn.edu.hziee.config.JWTUtil;import com.auth0.jwt.exceptions.*;import com.fasterxml.jackson.databind.ObjectMapper;import lombok.Data;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.util.HashMap;import java.util.Map;@Data@Componentpublic class JWTInterceptor implements HandlerInterceptor { private InterceptorProperties interceptorProperties; private static JWTUtil jwtUtil; @Autowired private void setJWTUtil(JWTUtil jwtUtil){ JWTInterceptor.jwtUtil = jwtUtil; } public JWTInterceptor(InterceptorProperties interceptorProperties) { this.interceptorProperties = interceptorProperties; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("token"); Map<String, Object> map = new HashMap<>(); try{ String secretKey = interceptorProperties.getSecretKey(); jwtUtil.verify(token); return true; } catch (InvalidClaimException e){ map.put("msg", "失效的payload异常"); e.printStackTrace(); } catch (JWTDecodeException e){ map.put("msg", "JWT解码异常"); e.printStackTrace(); }catch (SignatureVerificationException e){ map.put("msg", "签名不一致"); e.printStackTrace(); }catch (TokenExpiredException e){ map.put("msg", "令牌过期异常"); e.printStackTrace(); }catch (AlgorithmMismatchException e){ map.put("msg", "算法不匹配异常"); e.printStackTrace(); }catch (Exception e){ map.put("msg", "token无效"); e.printStackTrace(); } map.put("state", false); String json = new ObjectMapper().writeValueAsString(map); response.setContentType("application/json;charset=UTF-8"); response.getWriter().println(json); return false; }}
-
InterceptorConfig
package cn.edu.hziee.config;import cn.edu.hziee.Interceptors.InterceptorProperties;import cn.edu.hziee.Interceptors.JWTInterceptor;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class InterceptorConfig implements WebMvcConfigurer { @Autowired private InterceptorProperties interceptorProperties; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new JWTInterceptor(interceptorProperties)) .addPathPatterns("/user/tomain") .excludePathPatterns("/user/login"); }}
-
用接口 Postman 进行接口测试
token 由前端存储在 localStorage 中
《新程序员》:云原生和全面数字化实践 50位技术专家共同创作,文字、视频、音频交互阅读