JWT(JSON Web Token)作用
是一种用于在网络应用环境间传递声明的一种紧凑的、URL安全的令牌格式。
1. 身份验证:
- 用户登录后,服务器会生成一个JWT,并将其发送回用户。
- 用户随后可以将JWT存储在本地(通常是LocalStorage、SessionStorage或Cookies),并在随后的每个请求中将其作为请求头发送回服务器。
- 服务器通过验证JWT来确认用户的身份。
2. 信息交换:
- JWT可以安全地在客户端和服务器之间传递信息,因为JWT可以被签名,确保信息不被篡改。
3. 无状态和可扩展性:
- 由于服务器不需要存储会话信息,JWT使得系统更容易扩展。每次请求都可以独立地验证,不需要查询数据库。
4. 跨域认证:
- JWT可以安全地用于跨域认证,因为它可以被签名,确保令牌的真实性和完整性。
5. 单点登录(SSO):
- 用户在多个系统中只需要登录一次,就可以访问所有系统,因为JWT可以被多个系统共享。
6. 移动和桌面应用认证:
- 在移动或桌面应用中,JWT可以作为一种轻量级的方式来处理用户认证。
7. 授权:
- JWT可以包含角色或权限信息,用于控制用户对资源的访问。
8. 减少服务器请求:
- 由于JWT自包含用户信息,服务器不需要每次都查询数据库来获取用户信息。
9. 分布式系统:
- 在分布式系统中,JWT可以用于服务之间的安全通信。
10. API安全:
- 通过使用JWT,API可以安全地验证请求是否来自合法的用户。
11. 会话管理:
- JWT可以用于替代传统的会话cookie,提供无状态的会话管理。
12.令牌续期:
- 通过刷新令牌(refresh token)机制,可以延长用户的登录状态,而无需用户重新登录。
JWT的灵活性和多功能性使其成为现代网络应用中广泛使用的认证和授权机制。然而,需要注意的是,JWT不应包含敏感信息,因为其载荷部分可以被解码查看,除非使用加密算法对其进行加密,因为在网页中F12打开开发者模式可以看到相关相求的信息,如果信息敏感或被获取解密哦!!!
以下是可以解析Token的网址可供开发者排错:
Token解析小工具https://jwt.io/
token认证过程
1.首先前端登录的时候用户会输入用户名以及密码,服务器读取用户名以及密码去数据库验证是否正确
2.验证成功后生成token信息,同时用户也就就登录成功了,之后把token携带在请求头或者参数中,一般是请求头携带
3.返回token给前端
4.前端请求其他请求的时候就可以携带token相关信息去进行验证,此时会设置一个tokenFilter过滤器,验证token是否是正确的;token是否过期;token是否失效等等问题
5验证成功后返回请求数据
token过滤器
package com.manong.config.security.filter;
import com.manong.config.redis.RedisService;
import com.manong.config.security.exception.CustomerAuthenticationException;
import com.manong.config.security.handler.LoginFailureHandler;
import com.manong.config.security.service.CustomerUserDetailsService;
import com.manong.utils.JwtUtils;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Data
@Component
public class CheckTokenFilter extends OncePerRequestFilter {
@Value("${request.login.url}")
private String loginUrl;
@Resource
private RedisService redisService;
@Resource
private JwtUtils jwtUtils;
@Resource
private CustomerUserDetailsService customerUserDetailsService;
@Resource
private LoginFailureHandler loginFailureHandler;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
//获取当前请求的URL请求
String url = request.getRequestURI();
//判断当前请求地址是否是登录请求,如果不是登录请求,则需要验证token
if(!url.startsWith(loginUrl)) {
//进行token验证
this.validateToken(request);
}
} catch (AuthenticationException e) {
loginFailureHandler.onAuthenticationFailure(request,response,e);
}
//登录请求不需要token验证
doFilter(request, response, filterChain);
}
/**
* 验证token信息
* @param request
*/
private void validateToken(HttpServletRequest request) throws AuthenticationException {
// 从头部获取token信息
String token = request.getHeader("token");
if (ObjectUtils.isEmpty(token)) {
// 如果请求头部没有获取到token,则从请求的参数中进行获取
token = request.getParameter("token");
}
// 如果请求参数中也不存在token信息,则抛出异常
if (ObjectUtils.isEmpty(token)) {
throw new CustomerAuthenticationException("token不存在");
}
// 判断redis中是否存在该token
String tokenKey = "token_" + token;
String redisToken = redisService.get(tokenKey);
// 如果redis里面没有token,说明该token失效
if (ObjectUtils.isEmpty(redisToken)) {
throw new CustomerAuthenticationException("token已过期");
}
// 如果token和Redis中的token不一致,则验证失败
if (!token.equals(redisToken)) {
throw new CustomerAuthenticationException("token验证失败");
}
// 如果存在token,则从token中解析出用户名
String username = jwtUtils.getUsernameFromToken(token);
// 如果用户名为空,则解析失败
if (ObjectUtils.isEmpty(username)) {
throw new CustomerAuthenticationException("token解析失败");
}
// 获取用户信息
UserDetails userDetails = customerUserDetailsService.loadUserByUsername(username);
// 判断用户信息是否为空
if (userDetails == null) {
throw new CustomerAuthenticationException("token验证失败");
}
// 创建身份验证对象
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 设置身份验证对象到安全上下文中
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}