1.jwt是什么为啥要使用jwt
2.融入spring boot 项目,编写认证思路
2.1 用户登陆成功
2.2 根据成功用户信息生成对应的jwt认证信息
2.3 将认证信息返回给页面
2.4 页面存认证信息
2.5 其他请求在头部信息带上认证信息(这里可以约定放哪里,不一定在头部)
2.6 后端统一在过滤器认证登陆信息
2.7 认证信息解密后再次存起来好在后面具体业务使用
3.先引入jwt和fastjson的maven地址,fastjson在序列号的时候使用其他比较方便。
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.9</version>
</dependency>
4.编写jwt的工具类JwtUtil和用于存中间转化的类UserRole,其中setExpiration 可以设置过期时间,到时候认证过的信息自动失效。
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.security.Key;
import java.util.ArrayList;
import java.util.Date;
/**
* jwt 工具类 用户生成token 解密token
*/
public class JwtUtil {
// step.1 获取密钥 可以配置
private static final Key KEY = new SecretKeySpec("dkdi38393kd".getBytes(),
SignatureAlgorithm.HS512.getJcaName());
public static String getToken(UserRole userRole){
// step 3 主体存储信息
if(userRole.getRoles() == null){
userRole.setRoles(new ArrayList<>());
}
// 为什么要转成拼接字符串 会比直接json 体积小一点点 。。。。。
String payload = userRole.getUserId() + ":" + userRole.getRoles();
// step 4. 生成taken setExpiration设置过期时候 到时间自动过期
return "Bearer " + Jwts.builder().setExpiration(new Date(System.currentTimeMillis() + 1*24*60*60*100)).setPayload(payload).signWith(SignatureAlgorithm.HS512, KEY).compact();
}
public static String decryptTokenStr(String compactJws){
compactJws = compactJws.replaceFirst("Bearer ","");
String payload = Jwts.parser().setSigningKey(KEY).parse(compactJws).getBody().toString();
System.out.println();
return payload;
}
public static UserRole decryptToken(String payload){
String[] split = payload.split(":");
UserRole userRole = new UserRole();
userRole.setUserId(Long.parseLong(split[0]));
userRole.setRoles(JSON.parseArray(split[1], Long.class));
return userRole;
}
// 通过 servlet 参数 获取登陆用户信息 不用总是解密
public static UserRole decryptTokenByServlet(){
HttpServletRequest request =((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
String userName = request.getHeader("userName");
return decryptToken(userName);
}
}
import java.util.List;
/**
* 用户信息类,使用的时候比较方便
*/
public class UserRole {
/**
* 用户id
*/
private Long userId;
/**
* 角色id
*/
private List<Long> roles;
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public List<Long> getRoles() {
return roles;
}
public void setRoles(List<Long> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "UserRole{" +
"userId=" + userId +
", roles=" + roles +
'}';
}
}
5.编写登陆方法并且放回认证后的token。
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RestController
public class UserController {
@RequestMapping("user.login")
public String login(@RequestBody UserLoginVo loginVo) {
// 后期改为走数据库 校验 为了测试可以写死
if("admin".equals(loginVo.getUserName()) && "admin".equals(loginVo.getPassWord())){
UserRole userRole = new UserRole();
userRole.setUserId(0L);
userRole.setRoles(Arrays.asList(1L,2L));
return JwtUtil.getToken(userRole);
}
return "";
}
}
6.编写过滤器认证信息,这里的思路是先解密就解密后用户信息字符串存到servlet的head里面key值叫userName里面(可以自己取名字,获取的时候保持统一就行),在后续的业务代码不用再次解密,通过userName就可以直接转成用户信息类。
使用过滤器记得在启动类加上该注解,basePackages的值为过滤器所在包
@ServletComponentScan(basePackages = "com.dh.test")
import org.apache.commons.lang3.StringUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@WebFilter(urlPatterns = "/*")
public class RequestFilter implements Filter {
// 不校验的接口的集合 后期可以动态修改
private static final List<String> noVerificationList = new ArrayList<>();
static {
noVerificationList.add("/user.login");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
// 这个变量 请求放到那个参数里面 就去那个参数里面的
String authorization = request.getHeader("Authorization");
String requestURI = request.getRequestURI();
// 判断 接口是否是白名单
if (noVerificationList.contains(requestURI)) {
filterChain.doFilter(request, servletResponse);
return;
}
if(StringUtils.isEmpty(authorization)){
throw new RuntimeException("未登陆或登陆已经过期请重新登陆");
}
try{
// 解密设置到 head里面是为了 防止后面多次解密
RequestHeadWarp requestHeadWarp = new RequestHeadWarp(request);
String payload = JwtUtil.decryptTokenStr(authorization);
requestHeadWarp.addHead("userName",payload);
// 这里得用 重写后的RequestHeadWarp
filterChain.doFilter(requestHeadWarp, servletResponse);
}catch (Exception e){
throw new RuntimeException("未登陆或登陆已经过期请重新登陆");
}
}
}
8.因为servlet没有addHead的方法,所以重写HttpServletRequestWrapper的方法在过滤器doFilter的时候传入重写好后的类。
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class RequestHeadWarp extends HttpServletRequestWrapper {
/**
* 自定义的头部信息
*/
private Map<String, String> customHeaders = new HashMap<>();
/**
* 自定义的头部key集合 在getHead 判断的时候可以不用将map转再判断
*/
private Set<String> customHeaderNames = new HashSet<>();
public RequestHeadWarp(HttpServletRequest request) {
super(request);
}
@Override
public String getHeader(String name) {
if (customHeaderNames.contains(name)) {
return customHeaders.get(name);
}
return super.getHeader(name);
}
public void addHead(String key,String value){
customHeaders.put(key,value);
customHeaderNames.add(key);
}
}
9.编写测试接口看是否能正常解析token已经获取到用户信息。
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class TestController {
@RequestMapping("test.get")
public UserRole getUser(@RequestBody Map<String,String> map) {
// 这里 只是测试 获取登陆后的用户信息,可以加入自己的业务代码
return JwtUtil.decryptTokenByServlet();
}
}
10.postman测试登陆接口。
11.postman测试其他接口,看是否能正常拦截未登陆和登陆
11.1 先是未登陆,能看到正常抛出错误,提示未登陆。
11.2 head 加入登陆认证的token后再次请求,能正常获取到登陆的用户信息。
什么是token引入地址:什么是 JWT -- JSON WEB TOKEN - 简书