1 JWT 工具类
1.1 引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
1.2 创建 JwtUtil
package util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* Created by Administrator on 2018/4/11.
*/
@ConfigurationProperties("jwt.config")
public class JwtUtil {
private String key ;
private long ttl ;//一个小时
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public long getTtl() {
return ttl;
}
public void setTtl(long ttl) {
this.ttl = ttl;
}
/**
* 生成JWT
*
* @param id
* @param subject
* @return
*/
public String createJWT(String id, String subject, String roles) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
JwtBuilder builder = Jwts.builder().setId(id)
.setSubject(subject)
.setIssuedAt(now)
.signWith(SignatureAlgorithm.HS256, key).claim("roles", roles);
if (ttl > 0) {
builder.setExpiration( new Date( nowMillis + ttl));
}
return builder.compact();
}
/**
* 解析JWT
* @param jwtStr
* @return
*/
public Claims parseJWT(String jwtStr){
return Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(jwtStr)
.getBody();
}
}
思路:
- JWT 工具类,无非两个内容:生成 JWT、解析 JWT。
- 生成 JWT 的方法,需要 id、主题 subject、发布此 JWT 的时间(保证 JWT 不重复)、签证(传入签名算法和盐 key);由于本实例做的是不同用户的鉴权问题,这里指定
admin
用户才有权限签发 token,于是我们自定义一个claim
;并且还需要设置一个过期时间,它最好体现在配置文件中,于是我们多创建一个变量 ttl(time to live)。 - 由于我们需要的 JWT 是一个字符串,用
compact()
方法生成就行。 - 解析 JWT 的方法,先用
parser()
方法,然后传入盐和 JWT 字符串,最后getBody()
。
1.3 properties.yml 配置文件
在配置文件中写入盐 key 和 过期时间 ttl。
jwt:
config:
key: helloWorld
ttl: 360000
2 签发 token
2.1 注入 JwtUtil 到容器
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
@Bean
public IdWorker idWorker() {
return new IdWorker(1, 1);
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public JwtUtil jwtUtil() {
return new JwtUtil();
}
}
2.2 生成 token
调用 JWT 工具类,传入 id、登录名、角色,然后存在一个 map 中返回给前端。
@Autowired
private JwtUtil jwtUtil;
@PostMapping(value = "/login")
public Result login(@RequestBody Admin admin) {
Admin adminLogin = adminService.login(admin);
if (adminLogin == null) {
return new Result(StatusCode.ERROR, false, "登录失败");
}
// 使得前后端可以通话的操作,采用JWT来实现
// 生成令牌
String token = jwtUtil.createJWT(adminLogin.getId(), adminLogin.getLoginname(), "admin");
Map<String , Object> map = new HashMap<>();
map.put("token", token);
map.put("role", "admin");
return new Result(StatusCode.OK, true, "登录成功", map);
}
2.3 结果
可以从前端看到我们生成的 token 了。
3 解析 token
需求:删除用户,必须拥有管理员权限,否则不能删除。
前后端约定:前端请求微服务时需要添加头信息 Authorization,内容为 Bearer+空格+token。
3.1 判断请求头
修改 UserService 的 deleteUserById 方法 ,判断请求中的头信息,提取 token 并验证权限。
@Autowired
JwtUtil jwtUtil;
@Autowired
HttpServletRequest request;
public void deleteUserById(String userId) {
String header = request.getHeader("Authorization");
if (header == null || "".equals(header)) {
throw new RuntimeException("没有Authorization请求头");
}
if (!header.startsWith("Bearer")) {
throw new RuntimeException("Authorization非标准格式");
}
String token = header.substring(7);
try {
Claims claims = jwtUtil.parseJWT(token);
String roles = (String) claims.get("roles");
if (null == roles || !roles.equals("admin")) {
throw new RuntimeException("用户角色错误");
}
} catch (Exception e) {
throw new RuntimeException("token错误");
}
userDao.deleteById(userId);
}
3.2 测试
原本的数据库用的 user 表内容:
(1)没有加 header 的时候:
(2)Authorization 不是按照格式来的时候:
(3)token 格式错误的时候:
(4)填入正确 token 的时候: