赞
踩
JWT 全称 Json Web token,它将用户信息加密到 token 当中,服务端不保存任何用户信息。当服务端发送请求时,服务器通过保存的密钥验证 token 的正确性,正确则放行。
优点
使用简洁,数据量小,传输速度快,可以直接在 HTTP header 中发送
token 中包含了用户所需要的信息,避免了多次查询数据库
token 以 JSON 加密的形式在客户端存储,不需要在服务端保持会话
缺点
已经颁布的令牌无法作废
不易处理数据过期的问题
token 主要由 3 部分构成,分别为 header、payload 以及 signature,三部分之间以`.``分割,例如:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
.eyJhdWQiOiJhZG1pbiIsImV4cCI6MTY2NDQxMTI4Mn0
.cNvdGNNsNl8UJX_PIz40h9iJtDWexoymnC3h_b42iMA
header
声明类型,标识这是一条 JWT 信息
声明 JWT 加密的算法
payload
signature
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
配置@LoginToken
注解,标识需要进行 JWT 验证才能访问的方法
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginToken {
boolean required() default true;
}
通过拦截器对请求进行拦截,判断请求访问的方法和注解,根据条件对 token 进行验证
tips:ApplicationContextUtil 是上下文工具类,可自行搜索…
@Slf4j public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 获取业务类 UserService userService = ApplicationContextUtil.getBean(UserService.class); // 获取请求头中的 token String token = request.getHeader("token"); // 如果不是映射到方法的,直接通过 if (!(handler instanceof HandlerMethod)) { return true; } // 获取请求的方法 HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); // 验证是否存在需要验证用户权限的注释 if (method.isAnnotationPresent(LoginToken.class)) { if (method.getAnnotation(LoginToken.class).required()) { // 执行认证 if (token == null) { log.error("token 不存在,请重新登录"); throw new RuntimeException("token 不存在,请重新登录"); } // 获取 userId String userId; try { userId = JWT.decode(token).getAudience().get(0); } catch (JWTDecodeException e) { log.error("token 解码失败"); throw new RuntimeException("token 解码失败"); } // 根据 userId 查询 User User user = userService.getUser(userId); if (user == null) { log.error("用户不存在,请重新登录"); throw new RuntimeException("用户不存在,请重新登录"); } // 验证 token JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build(); try { jwtVerifier.verify(token); } catch (JWTVerificationException e) { log.error("token 校验失败"); throw new RuntimeException("token 校验失败,请重新登录"); } return true; } } return true; } }
拦截器配置类
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
/**
* 注册 JWT 拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JwtInterceptor()).addPathPatterns("/**");
}
}
全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
@ResponseBody
@ExceptionHandler(Exception.class)
public Object handleException(Exception e) {
String msg = e.getMessage();
if (msg == null || "".equals(msg)) {
msg = "服务器出错";
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", 50000);
jsonObject.put("message", msg);
return jsonObject;
}
}
构造tokenService
,对用户的信息执行注册,返回对应用户的 token
@Service public class TokenService { /** * 设置过期时间 */ private static final long EXPIRE_TIME = 5 * 60 * 1000; /** * 获取 token */ public String getToken(User user) { Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); return JWT.create() // 将 userId 保存在 token 中 .withAudience(user.getUserId()) // 设置过期时间 .withExpiresAt(date) // 将 password 设置为 token 的密钥 .sign(Algorithm.HMAC256(user.getPassword())); } }
构造userService
,模拟从数据库中查询对应用户信息的过程
@Service public class UserService { /** * 模拟查询参数 */ private final static String PARAM = "admin"; /** * 根据 id 查询 User */ public User getUser(String userId) { if (PARAM.equals(userId)) { return User.builder() .userId(userId) .username("admin") .password("admin") .build(); } return null; } /** * 根据用户名查询 User */ public User getUser(String username, String password) { if (PARAM.equals(username)) { return User.builder() .userId("admin") .username(username) .password(password) .build(); } return null; } }
@RestController @RequestMapping("/JWT") public class LoginController { @Resource private UserService userService; @Resource private TokenService tokenService; /** * 登录 */ @PostMapping("/sign") public Object sign(String username, String password){ JSONObject jsonObject = new JSONObject(); User user = userService.getUser(username, password); if (user == null) { jsonObject.put("message", "登录失败!"); } else { String token = tokenService.getToken(user); jsonObject.put("token", token); jsonObject.put("user", user); } return jsonObject; } /** * 验证登录 */ @LoginToken @PostMapping("/getMessageWithToken") public String getMessageWithToken(){ return "已通过验证,允许获取信息~"; } /** * 验证登录 */ @LoginToken(required = false) @PostMapping("/getMessageWithoutToken") public String getMessageWithoutToken(){ return "无需验证即可获取对应信息~"; } }
首先测试不携带 token 访问getMessageWithToken
与getMessageWithoutToken
接口
getMessageWithToken
未携带 token,禁止访问
getMessageWithoutToken
不需要 token 认证即可访问
访问sign
接口执行注册,获得返回的 token
携带 token 重新访问getMessageWithToken
,可正确执行访问
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。