1.新建springboot应用,添加依赖
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.6.7
com.zsp
jwt_springboot
0.0.1-SNAPSHOT
jwt_springboot
jwt_springboot
1.8
org.springframework.boot
spring-boot-starter
com.baomidou
mybatis-plus-boot-starter
3.2.0
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
com.auth0
java-jwt
3.4.0
org.springframework.boot
spring-boot-maven-plugin
org.projectlombok
lombok
2.编写配置文件,连接数据库,定义端口,定义mapper映射
server.port=8082
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mypractice?serverTimezone=Asia/Shanghai&autoReconnect=true&useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456
mybatis-plus.mapper-locations=classpath:mapper/*.xml
mybatis-plus.type-aliases-package=com.zsp.entity
3.整体目录
controller层,定义两个接口。登录接口和验证测试接口。代码里调用了jwtUtils工具类,
//生成JWT令牌
String token = jwtUtils.getToken(payload);
自己定义的,在后面有说明。
package com.zsp.controller;
import com.baomidou.mybatisplus.extension.api.ApiController;
import com.zsp.entity.Users;
import com.zsp.service.UserService;
import com.zsp.utils.jwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class UserController extends ApiController {
@Autowired
private UserService userService;
@GetMapping("/user/login")
public Map login(Users users){
Map map=new HashMap<>();
try {
if (users.getUsername()==null || users.getPassword()==null){
map.put("state", false);
map.put("msg", "账户或密码为空");
return map;
}
Users usersDB = userService.login(users);
Map payload = new HashMap<>();
payload.put("id", String.valueOf(usersDB.getId()));
payload.put("name", usersDB.getUsername());
//生成JWT令牌
String token = jwtUtils.getToken(payload);
map.put("state", true);
map.put("msg", "认证成功");
map.put("token", token);
} catch (Exception e){
map.put("state", false);
map.put("msg", e.getMessage());
}
return map;
}
@PostMapping("/user/test")
public Map test(){
Map map=new HashMap<>();
//处理自己的业务逻辑
map.put("state",true);
map.put("msg","请求成功");
return map;
}
}
Dao层定义登录方法
UserDao层
package com.zsp.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zsp.entity.Users;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserDao extends BaseMapper {
//用户登录
Users login(Users users);
}
UserDao.XML
entity层
package com.zsp.entity;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import java.io.Serializable;
/**
* (User)表实体类
*
* @author makejava
* @since 2022-05-19 15:20:46
*/
@SuppressWarnings("serial")
public class Users extends Model {
private Integer id;
private String username;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
/**
* 获取主键值
*
* @return 主键值
*/
@Override
protected Serializable pkVal() {
return this.id;
}
}
service层和serviceImpl
package com.zsp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zsp.entity.Users;
/**
* (User)表服务接口
*
* @author makejava
* @since 2022-05-19 15:20:46
*/
public interface UserService extends IService {
//用户登录
Users login(Users users);
}
package com.zsp.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zsp.dao.UserDao;
import com.zsp.entity.Users;
import com.zsp.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* (User)表服务实现类
*
* @author makejava
* @since 2022-05-19 15:20:46
*/
@Service("userService")
@Transactional
public class UserServiceImpl extends ServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public Users login(Users users) {
Users login = userDao.login(users);
if (login != null) {
return login;
}
throw new RuntimeException("登录失败");
}
}
以上就是基本的业务代码。
下面开始做JWT工具类,也就是utils包下的工具类。
package com.zsp.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Map;
public class jwtUtils {
//自己定义的签名,可以更改 !Q@W#E$R121121
private static final String SIGN="!Q@W#E$R121121";
//JWT生成Token
public static String getToken(Map map){
Calendar instance=Calendar.getInstance();
instance.add(Calendar.DATE,7); //默认7天过期
JWTCreator.Builder builder = JWT.create();
map.forEach((k,v)->{
//payload
builder.withClaim(k,v);
});
String token = builder.withExpiresAt(instance.getTime()) //过期时间
.sign(Algorithm.HMAC256(SIGN));//signature 签名
return token;
}
// JWT验证token合法性,并获取信息
public static DecodedJWT verify(String token){
//创建验签对象
DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
return verify;
}
//获取token信息的方法
// public static DecodedJWT getTokenInfo(String token){
// //创建验签对象
// DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
// return verify;
// }
}
具体来讲工具类就是:
首先:
jwt 分为三部分:
header payload signature
首先定义一个签名,用来做验证。因为服务器通过jwt生成token令牌发给客户端 或者 服务器验证客户端携带的token的请求时,都需要用到这个签名。
原理:客户端请求服务器认证,服务器生成JWT(Token)令牌交给客户端保存,客户端存入header中的Author中。每次请求就携带这个Author
参数传map类型是因为 payload中的 withClaim方法,接收key,value形式
然后就是传入过期时间和签名。相当于以下这种写法,只不过下面这种的payload是写死的,上面的用map来让用户自定义的。
接着就是jwt验证token,也就是你客户端发过来消息,走到我接口这我先验证你这个token是不是我发给你的,有没有被篡改,这个时候签名就是自己定义的。
JWT.require方法进行验签,接收前端传来的token和指定的签名。
jwt验证已经做好了,接下来就是何时进行验证。当前端请求掉后台接口时,通过拦截器的方式看是否携带token。现在做拦截器
先写一个拦截规则。怎么拦截
在前端调用后台controller时进行拦截
package com.zsp.Interceptor;
//怎么拦截
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zsp.utils.jwtUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Map map=new HashMap<>();
//获取前端header头携带的token
String token = request.getHeader("token");
try {
jwtUtils.verify(token);//验证令牌
return true;//放行请求
} catch (SignatureVerificationException e) {
e.printStackTrace();
map.put("msg","拦截器拦截:无效签名");
}catch (TokenExpiredException e) {
e.printStackTrace();
map.put("msg","拦截器拦截:token过期");
}catch (AlgorithmMismatchException e) {
e.printStackTrace();
map.put("msg","拦截器拦截:token算法不一致");
}catch (Exception e) {
e.printStackTrace();
map.put("msg","拦截器拦截:token无效");
}
map.put("state",false);//设置状态
//将map转为json jackson
String json = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
return false;
}
}
再写一个拦截哪些
package com.zsp.Interceptor;
//拦截哪些
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
//覆盖父类中添加拦截器的方法
//参数1:InterceptorRegistry 拦截器注册对象
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()) //这里是刚才创建的拦截器
.addPathPatterns("/user/test") //拦截哪些 拦截所有
.excludePathPatterns("/user/login");//排除哪些 排除login
//添加拦截器
}
}
最后进行测试。Postman
首先调用login,匹配数据库是否存在,
Users usersDB = userService.login(users);
如果存在,注入payload
payload.put("id", String.valueOf(usersDB.getId())); payload.put("name", usersDB.getUsername());
调用工具类生成令牌的方法返回前端
此时生成了token。
接着调用后台测试接口。
需要在header头里面加入生成的token
验证通过。