新项目实现JWT接口(详细步骤)
1、用springboot脚手架构建项目https://start.spring.io2、项目导入IDE3、导入相关依赖包包<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version>
·
下载demo项目: https://download.csdn.net/download/chw0629/21798838
1、用springboot脚手架构建项目
https://start.spring.io
2、项目导入IDE
3、导入相关依赖包包
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
4、创建实体类
R.java //客户端请求返回对象
package com.yuer629.jwtdemo.entity;
import java.util.HashMap;
import java.util.Map;
/**
* 返回数据
*/
public class R extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
public R() {
put("code", 0);
}
public static R error() {
return error(500, "未知异常,请联系管理员");
}
public static R error(String msg) {
return error(500, msg);
}
public static R error(int code, String msg) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static R ok(String msg) {
R r = new R();
r.put("msg", msg);
return r;
}
public static R ok(Map<String, Object> map) {
R r = new R();
r.putAll(map);
return r;
}
public static R ok() {
return new R();
}
@Override
public R put(String key, Object value) {
super.put(key, value);
return this;
}
}
User.java //用户对象
package com.yuer629.jwtdemo.api.jwt.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Objects;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
String Id;
String username;
String password;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return username.equals(user.username) &&
password.equals(user.password);
}
@Override
public int hashCode() {
return Objects.hash(username, password);
}
}
VirtualDB.java //模拟数据库
package com.yuer629.jwtdemo.api.jwt.entity;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
/**
* 模拟数据库 操作
*/
public class VirtualDB {
//构造模拟数据
private final static List<User> USER_LIST = Arrays.asList(
new User[]{
new User("1","张三","zhangsan"),
new User("2","李四","lisi"),
new User("3","王五","ww123")
}
);
/**
* 根据用户名查询
*/
public static User selectByUsername(String username){
for(User u : USER_LIST){
if(u.getUsername().equals(username)){
return u;
}
}
return null;
}
/**
* 根据ID查询User
* @param id
*/
public static User selectById(Serializable id){
for(User u : USER_LIST){
if(u.getId().equals(id+"")){
return u;
}
}
return null;
}
}
PassToken.java //此注解用于绕开Token登陆验证
package com.yuer629.jwtdemo.api.jwt.entity;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
boolean required() default true;
}
UserLoginToken.java //此注解用于校验Token登陆验证
package com.yuer629.jwtdemo.api.jwt.entity;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
boolean required() default true;
}
5、拦截器相关
AuthenticationInterceptor.java //拦截器处理类
package com.yuer629.jwtdemo.api.jwt.Interceptor;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.yuer629.jwtdemo.api.jwt.entity.PassToken;
import com.yuer629.jwtdemo.api.jwt.entity.User;
import com.yuer629.jwtdemo.api.jwt.entity.UserLoginToken;
import com.yuer629.jwtdemo.api.jwt.entity.VirtualDB;
import com.yuer629.jwtdemo.exception.RRException;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {
@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 (StringUtils.isBlank(token)) {
throw new RRException("请在Header中携带token参数及相应的值");
}
// 获取 token 中的 user id
String userId;
try {
userId = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
throw new RRException("token解密失败,请检查token值");
}
//TODO 去数据库查询用户
User user = VirtualDB.selectById(userId);
if (user == null) {
throw new RRException("用户不存在,请重新登录");
}
// 验证 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try {
jwtVerifier.verify(token);
} catch (JWTVerificationException e) {
throw new RRException("token验证失败,请重新获取");
}
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 {
}
}
InterceptorConfig.java //拦截器配置类
package com.yuer629.jwtdemo.config;
import com.yuer629.jwtdemo.api.jwt.Interceptor.AuthenticationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
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 {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**"); // 拦截所有请求,通过判断是否有 @UserLoginToken 注解 决定是否需要登录
}
@Bean
public AuthenticationInterceptor authenticationInterceptor() {
return new AuthenticationInterceptor();
}
}
6、异常处理相关
RRException.java // 自定义异常类
package com.yuer629.jwtdemo.exception;
/**
* 自定义异常
*/
public class RRException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String msg;
private int code = 500;
public RRException(String msg) {
super(msg);
this.msg = msg;
}
public RRException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
public RRException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}
public RRException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
RRExceptionHandler.java // 异常处理器(统一处理)
package com.yuer629.jwtdemo.exception;
import com.yuer629.jwtdemo.entity.R;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.yaml.snakeyaml.constructor.DuplicateKeyException;
/**
* 异常处理器
*/
@RestControllerAdvice
public class RRExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 处理自定义异常
*/
@ExceptionHandler(RRException.class)
public R handleRRException(RRException e) {
R r = new R();
r.put("code", e.getCode());
r.put("msg", e.getMessage());
return r;
}
@ExceptionHandler(DuplicateKeyException.class)
public R handleDuplicateKeyException(DuplicateKeyException e) {
logger.error(e.getMessage(), e);
return R.error("数据库中已存在该记录");
}
@ExceptionHandler(Exception.class)
public R handleException(Exception e) {
logger.error(e.getMessage(), e);
return R.error();
}
}
7、TokenService.java
package com.yuer629.jwtdemo.api.jwt;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.yuer629.jwtdemo.api.jwt.entity.User;
import com.yuer629.jwtdemo.entity.R;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class TokenService {
public static final long EXPIRATION_TIME = 2*60*60*1000L; // 过期时间:2 hour
public R getToken(User user) {
String token="";
Long expirtesTime = System.currentTimeMillis() + EXPIRATION_TIME;
token= JWT.create()
.withAudience(user.getId())
.withSubject(user.getUsername())
.withExpiresAt(new Date(expirtesTime))
.withIssuedAt(new Date())
.sign(Algorithm.HMAC256(user.getPassword()));
return R.ok().put("exp",expirtesTime).put("token",token);
}
}
8、jwt接口
JwtApi.java
package com.yuer629.jwtdemo.api.jwt;
import com.alibaba.fastjson.JSONObject;
import com.yuer629.jwtdemo.api.jwt.entity.User;
import com.yuer629.jwtdemo.api.jwt.entity.UserLoginToken;
import com.yuer629.jwtdemo.api.jwt.entity.VirtualDB;
import com.yuer629.jwtdemo.entity.R;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("jwt")
public class JwtApi {
@Autowired
TokenService tokenService;
//登录
@PostMapping("/login")
public R login(@RequestBody User user){
//检验参数的完整性
if(StringUtils.isBlank(user.getUsername()) || StringUtils.isBlank(user.getPassword())){
return R.error("参数不完整");
}
JSONObject jsonObject=new JSONObject();
//TODO 从数据库查询用户
User userForBase= VirtualDB.selectByUsername(user.getUsername());
if(userForBase==null){
return R.error("登录失败,用户不存在");
} else {
if (!userForBase.getPassword().equals(user.getPassword())){
//TODO 密码校验
return R.error("登录失败,密码错误");
} else {
R r = tokenService.getToken(userForBase);
return r.put("username",userForBase.getUsername());
}
}
}
@UserLoginToken
@GetMapping("/getMessage")
public R getMessage(){
return R.ok("你已通过验证");
}
}
9、配置文件允许SpringBean重写,在application.properties文件增加
spring.main.allow-bean-definition-overriding=true
10、postman 调用接口测试
curl: (可在命令行执行,或者粘贴到postman片段执行)
curl -X POST http://localhost:8080/jwt/login -H "Content-Type: application/json" -H "Postman-Token: 55bf274e-3e51-42b5-9d7a-1c9ec3550a0f" -H "cache-control: no-cache" -d "{\"username\":\"张三\",\"password\":\"zhangsan\"}"
登陆-正常
登陆-密码错误:
登陆-用户名错误:
其他接口-正常:
其他接口-无token:
其他接口-token错误:
其他接口-token过期:
更多推荐
已为社区贡献1条内容
所有评论(0)