0.前言
JWT的全称叫Json Web Token;在前端和后台进行数据交互的时候,在请求头中带上一个token 字符串,这个字符串中保存了用户具有的权限信息。同时也是经过加密的,不容易被破解。
在项目中,我们使用token来进行权限的鉴定。用户登录信息匹配的时候,我们就获取当前用户具有的api权限标识符。把这些标识符放在放在token中,并返回给客户端(token是进行过加密的,不用担心安全问题)。客户端发送第二请求的时候,就会吧这个token放在请求头中。服务器端的拦截器或者这个请求头信息,并解析出token中的api权限标识符,如果当前请求的标识可以在token中匹配到,那么就通过请求。这就是token鉴权的大致过程。
1.Maven引入JJWT
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2. 编写测试代码
public class CreateJwtTest {
/**
* 使用jjwt创建token
* @param
*/
@Test
public void test01() {
JwtBuilder jwtBuilder = Jwts.builder().setId("123")
.setSubject("alu4r")//用户相关信息
.setIssuedAt(new Date())//设置创建的时间
.signWith(SignatureAlgorithm.HS256, "alu4r")//为token加密
.claim("key","value");
Map<String, Object> map = new HashMap<>();
map.put("11", "11");
map.put("22", "22");
jwtBuilder.setClaims(map);
String token = jwtBuilder.compact();
System.out.println(token);
}
/**
* 解析jwt token字符串
*/
@Test
public void Test02(){
String token = "eyJhbGciOiJIUzI1NiJ9.eyIxMSI6IjExIiwiMjIiOiIyMiJ9.JIzRbr5R2D9cfNFgOscGM_PD_mXdW_zJGMWmmc8Ddgs";
Claims claims = Jwts.parser().setSigningKey("阿alu4r").parseClaimsJws(token).getBody();
//数据都放在了claims中
System.out.println(claims.getId()+"==>"+claims.getSubject()+"==>"+claims.getIssuedAt());
//获取自定义的claims
System.out.println(claims.get("11"));
}
}
3.jwt生成和解析token工具类
@Getter
@Setter
@EnableConfigurationProperties(JwtUtils.class)
@ConfigurationProperties(prefix = "jwt.config")
public class JwtUtils {
/**
* 签名私钥
*/
private String key;
/**
* 签名失效时间
*/
private Long ttl;
/**
* 解析token,获取claims
*/
public Claims parseJwt(String token) {
Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();
return claims;
}
/**
* 设置认证token
* id : 用户的id
* name : 用户名
* map : 用户的私有数据
*/
public String createJwt(String id, String subject, Map<String, Object> map){
//设置失效时间
//当前毫秒数
long now = System.currentTimeMillis();
//失效后的毫秒数
long exp = now + ttl;
//用户相关信息、设置创建的时间、为token加密
JwtBuilder jwtBuilder = Jwts.builder().setId(id)
.setSubject(subject)
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, key);
//添加私有map数据
for (Map.Entry<String, Object> entry : map.entrySet()) {
jwtBuilder.claim(entry.getKey(), entry.getValue());
}
//设置失效的时间
jwtBuilder.setExpiration(new Date(exp));
String token = jwtBuilder.compact();
return token;
}
}
工具类中使用了@ConfigurationProperties(prefix = “jwt.config”)注解,key和有效时间可以在配置文件(yml)中进行配置。
4. 在token中设置api权限
用户在登录成功的时候,我们可以在数据库中查询当前用户有哪些api权限,把这些api权限的标识放在token中;
登录控制器:
/**
*Result : 返回到客户端的数据
*Role : 角色
*Permission : 权限
*/
@PostMapping("/login")
public Result login(@RequestBody Map<String, String> loginMap){
String mobile = loginMap.get("mobile");
String password = loginMap.get("password");
User user = userService.findByMobile(mobile);
Result result = null;
if(user == null || !Objects.equals(password,user.getPassword())){
result = new Result(ResultCode.MOBILE_OR_PASSWORD);
}else {
//获取所有的可访问api权限
StringBuilder sb = new StringBuilder();
for (Role role : user.getRoles()) {
for (Permission permission : role.getPermissions()) {
if(permission.getType() == PermissionConstants.PERMISSION_API){
sb.append(permission.getCode()).append(",");
}
}
}
Map<String, Object> map = new HashMap<>();
map.put("apis", sb.toString());
map.put("companyId",user.getCompanyId());
map.put("companyName",user.getCompanyName());
//将apis的权限标识符,和其他的一些需要的数据放在token中
String token = jwtUtils.createJwt(user.getId(), user.getUsername(), map);
result = new Result(ResultCode.SUCCESS,token);
}
return result;
}
5. 解析获取token中的数据
在项目中我们把解析token的代码放在拦截器中,这样我们就可以对每一个请进行权限验证了
@Component
public class JwtInterceptor extends HandlerInterceptorAdapter {
@Autowired
JwtUtils jwtUtils;
/**
* 进入控制器之前执行的方法
* 简化获取token数据的代码的编写
* 同一的用户权限校验
*
* 判断用户是否有当前的接口权限
* @param request
* @param response
* @param handler
* @return 是否继续执行
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取到token
String authorization = request.getHeader("Authorization");
//判断请求头信息是否为空, 或者是否以Bearer开头
if(!StringUtils.isEmpty(authorization) && authorization.startsWith("Bearer ")){
String token = authorization.replace("Bearer ","");
Claims claims = jwtUtils.parseJwt(token);
if(claims != null){
String apis = (String) claims.get("apis");
//通过handler,获取控制器api接口名,也就是PostMapping注解中name中的属性
HandlerMethod handlerMethod = (HandlerMethod) handler;
RequestMapping methodAnnotation = handlerMethod.getMethodAnnotation(RequestMapping.class);
String name = methodAnnotation.name();
//如果匹配到权限标识符 就通过请求
if(apis.contains(name)){
//用来保存私有数据的,
request.setAttribute("user_claims",claims);
return true;
}else {
throw new CommonException(ResultCode.UNAUTHORISE);
}
}
}else {
throw new CommonException(ResultCode.UNAUTHENTICATED);
}
return true;
}
}
代码是截取项目中的部分,不能保证copy就能运行,不对之处,请多指教!