Jwt是用于前后端数据传输的安全性,话不多说,直击上思路!
一、项目背景
vue + springboot
二、后端实现
1、引入jwt依赖库
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.18.1</version> <!-- 请使用最新版本 -->
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.3</version> <!-- 使用你需要的版本 -->
</dependency>
2、分发令牌
1、让这个createToken方法接受一个类用于携带这个jwt中的用户信息
但切记不要携带密码,因为jwt是可以被解密的!
去网上直接搜jwt解密你会搜到很多网页!!
2、verifyToken是检验jwt是否存在或者合法的工具,用于第三步的过滤器
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.spring.props.billProps.User;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 密码
**/
private static final String SECRET = "密码";
/**
* 过期时间
**/
private static final long EXPIRATION = 1800L;
public static String createToken(User user) {
String permission = null;
// 重新定义分发userType
//过期时间
Date expireDate = new Date(System.currentTimeMillis() + EXPIRATION * 10000);//毫秒
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
String token = JWT.create()
.withHeader(map)// 添加头部
//可以将基本信息放到claims中
.withClaim("username", user.getUsername())
.withClaim("userType", user.getUserType())
.withClaim("permission",permission)
.withExpiresAt(expireDate) //超时设置,设置过期的日期
.withIssuedAt(new Date()) //签发时间
.sign(Algorithm.HMAC256(SECRET)); //SECRET加密
return token;
}
private static final Logger logger = LoggerFactory.getLogger(当前类名.class);
/**
* 校验token并解析token
*/
public static Map<String, Claim> verifyToken(String token) {
DecodedJWT jwt = null;
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
jwt = verifier.verify(token);
} catch (Exception e) {
logger.error(e.getMessage());
logger.error("token解码异常");
//解码异常则抛出异常
return null;
}
return jwt.getClaims();
}
3、过滤器
拦截前端传过来的请求,并验证Headers中Authorization里面的jwt
import com.auth0.jwt.interfaces.Claim;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Map;
@Slf4j
@Component
@WebFilter(filterName = "JwtFilter", urlPatterns = "/secure/*")
public class jwtFilter implements Filter
{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
response.setCharacterEncoding("UTF-8");
//获取 header里的token
final String token = request.getHeader("Authorization");
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
chain.doFilter(request, response);
}
下面这里用于直接通行某些请求,如登陆请求,就不需要jwt验证
// else if (request.getRequestURI().endsWith("/login/login")) {
// chain.doFilter(req, res);}
else {
if (token == null) {
response.getWriter().write("没有token!");
return;
}
Map<String, Claim> userData = 类名.verifyToken(token);
if (userData == null) {
response.getWriter().write("token不合法!");
return;
}
chain.doFilter(req, res);
}
}
@Override
public void destroy() {
}
}
4、具体应用
import static com.example.spring.Tools.Jwt.Util.createToken;
@PostMapping("/login")
public Result login(@RequestBody User user) {
User res = loginService.login(user);
if (res == null) {
return Result.error("该账号未被注册");
} else if (res.getStatue() == 1) {
if (user.getPhoneNumber() != null) {
String jwt = createToken(res);
return Result.success(jwt);
} else if (user.getPassword() != null) {
log.info(user.getPassword());
log.info(res.getPassword());
if (user.getPassword().equals(res.getPassword())) {
String jwt = createToken(res);
return Result.success(jwt);
} else {
return Result.error("密码错误");
}
} else {
return Result.error("登陆错误");
}
} else if (res.getStatue() == 0) {
return Result.error("该账户已停用");
} else {
return Result.error("登陆错误");
}
}
5、测试结果
如下data米面携带的信息即为携带我们信息的jwt令牌
三、前端实现
1、保存jwt令牌于localStorage
在前端执行完登陆操作后,如后端的测试结果一样我们会得到一个jwt令牌
localStorage.setItem('jwt', jwt)
2、给请求添加jwt令牌
在请求发出去之前我们需要拦截者请求
并给这个axios请求添加上一个名为Authorization的headers,内容就是jwt
import axios from "axios";
import { jwtDecode } from "jwt-decode";
import router from '@/router';
export const request = axios.create({
headers: { "Content-Type": "application/json" },
timeout: 20000,
});
request.interceptors.request.use(
(config) => {
const jwt = localStorage.getItem("jwt");
// 如果是登陆处的
if(如果当前页面是登陆界面则直接返回config不要添加jwt){
if (jwt) {
// 在此可以验证jwt是否存在或者是否过期
config.headers.Authorization = `${jwt}`;
} else {
// 返回登陆处
}
} else {
return config
}
return config
},
(error) => {
return Promise.reject(error);
}
);
export default request;