JTW实现spring后端token验证
基于之前的spring结合MongoDB的登录功能,添加了token验证的功能
上一个项目
首先展示一下项目的结构
#####项目依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.12.11</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>bson</artifactId>
<version>4.6.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.19.2</version>
</dependency>
</dependencies>
实体类部分
User.java
@Data
public class User{
private String username;
private String _id;
private String password;
private String token;//后端直接将token加入到实体类模型中
}
Msg.java
@Data
public class Msg<T> {
private Integer code;//状态码
private String msg;//信息
private T data;//数据
public static <T> Msg<T> success(T object){
Msg<T> r = new Msg<>();
r.data = object;
r.code = 1;
return r;
}
public static <T> Msg<T> error(String message){
Msg r = new Msg();
r.msg=message;
r.code = 0;
return r;
}
}
工具类
DBUtil.java
@Slf4j
@Service
public class DBUtils {
public static MongoCollection getCollection (String s){//根据传入的集合名称或许集合
MongoClient mongoClient = new MongoClient("localhost",27017);
MongoDatabase mongoDatabase = mongoClient.getDatabase("test");
log.info("获取数据库成功:"+mongoDatabase);
MongoCollection<Document> conn = mongoDatabase.getCollection(s);
log.info("获取集合成功:"+mongoDatabase);
return conn;
}
}
JWTUtils.java
public class JWTUtils {
public static String getToken(User user){
Calendar calendar = Calendar.getInstance();//Calender为一个日期操作的类
calendar.add(Calendar.DATE,1);//设置过期时间
JWTCreator.Builder builder = JWT.create();
builder.withClaim("_id",user.get_id())
.withClaim("username",user.getUsername());//写入数据
String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.HMAC256(SIGNATURE));//生成token
return token;
}
public static DecodedJWT verify(String token){
DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGNATURE)).build().verify(token);
return verify;
}
}
Service层无更改,实现登录验证功能即可
这里直接将实现的类代码展示出来
UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {
MongoCollection conn = DBUtils.getCollection("user");
@Override
public User findUserById(String _id) {
FindIterable<Document> iterable = conn.find(new Document("_id",_id));
MongoCursor<Document> cursor = iterable.iterator();
if(cursor.hasNext()) {
Document document = cursor.next();
return JSON.parseObject(document.toJson(), User.class);
} else{
return null;
}
}
}
之后是拦截器的配置
拦截器验证则是直接根据前端发送的token令牌进行验证
JWTInterceptor.java
@Slf4j
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
String token = request.getHeader("token");
try {
JWTUtils.verify(token);
}catch (SignatureVerificationException e){
e.printStackTrace();
log.info("无效签名");
return false;
}catch (TokenExpiredException e){
e.printStackTrace();
log.info("token过期");
return false;
}catch (AlgorithmMismatchException e){
e.printStackTrace();
log.info("token算法不一致");
return false;
}catch (Exception e){
e.printStackTrace();
log.info("token无效");
return false;
}
return true;
}
}
IntercaptorConfig.java
@Configuration
public class IntercaptorConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor((new JWTInterceptor()))
.addPathPatterns("/**")//拦截所有api
.excludePathPatterns("/login");//放行登录目录下的请求
}
}
控制层
UserController.java
@Slf4j
@RestController
public class UserController {
@Autowired
UserServiceImpl service ;
@PostMapping("/login")
public Msg<User> login(@RequestBody User user){
if(user!=null) log.info("后端成功接收User对象"+user);
User o = service.findUserById(user.get_id());
if(o==null){
log.info("用户名不存在");
return Msg.error("用户不存在");
}else {
if(o.getPassword().equals(user.getPassword())){
log.info("查询成功"+Msg.success(o));
o.setToken(JWTUtils.getToken(o));
o.setPassword(null);
return Msg.success(o);
}else {
log.info("密码错误,接受到的密码为:"+user.getPassword()+";正确密码为:"+o.getPassword());
return Msg.error("密码错误");
}
}
}
@PostMapping("/verify")
public String verify() {
return "验证成功";
}
项目测试
首先是直接访问Controller层写好的一个测试api
控制台给出了token无效的错误提示,说明拦截器成功拦截到了请求
之后进行一次正常登陆
这里已经可以得到后端返回的token了
然后使用返回的token再次验证
就可以正常访问了