1、 JWT工具类编写
(1)common工程引入依赖(考虑到工具类的通用性)
io.jsonwebtoken
jjwt
0.9.0
(2)修改common工程,创建util.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 java.util.Date;
@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) //用户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();
}
}
(3)修改user工程的application.yml, 添加配置
#jwt相关
jwt:
config:
key: justIT
ttl: 3600000
2、管理员登陆后台签发token
(1)配置bean,修改user工程Application类
@Bean
public JwtUtil jwtUtil(){
return new JwtUtil();
}
(2)修改AdminController的login方法
@Autowired
private JwtUtil jwtUtil;
/**
* @Description: //TODO 用户登录
* @Param: []
* @return: entity.Result
*/
@PostMapping("/login")
public Result login(@RequestBody Admin admin){
admin = adminService.login(admin);
if (admin == null){
return new Result(false, StatusCode.LOGINERROR, "登录失败");
}
//使得前后端通话
//生成令牌
Map map = new HashMap<>();
String token = jwtUtil.createJWT(admin.getId(), admin.getLoginname(), "admin");
map.put("roles", "admin");
map.put("token", token);
return new Result(true, StatusCode.OK, "登录成功", map);
}
测试运行结果:
{
"flag": true,
"code": 20000,
"message": "登陆成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI5ODQzMjc1MDc4ODI5MzgzNjgiLCJzdWIiOiJ4aWFvbWkiLCJpYXQiOjE1MjM1MjQxNTksInJvbGVzIjoiYWRtaW4iLCJleHAiOjE1MjM1MjQ1MTl9._YF3oftRNTbq9WCD8Jg1tqcez3cSWoQiDIxMuPmp73o",
"name":"admin"
}
}
3、删除用户功能鉴权
(1)需求:删除用户,必须拥有管理员权限,否则不能删除。
(2)前后端约定:前端请求微服务时需要添加头信息Authorization,内容为Bearer+空格 +token
(1)修改UserServer的deleteById方法 ,判断请求中的头信息,提取token并验证权限。
@Autowired
private HttpServletRequest request;
/**
* 删除,必须有admin角色
* @param id
*/
public void deleteById(String id) {
//获取请求头
String header = request.getHeader("Authorization");
if(header == null && !header.startsWith("Bearer ")){//没有头信息,且没按规定的格式开头
throw new RuntimeException("权限不足");
}
//得到token
String token = header.substring(7);
//对令牌进行验证
try {
Claims claims = jwtUtil.parseJWT(token);
//如果是管理员
if (claims == null && !"admin".equals(claims.get("roles"))){
throw new RuntimeException("权限不足");
}
}catch (Exception e){
throw new RuntimeException("令牌不正确");
}
userDao.deleteById(id);
}
4、使用拦截器方式实现token鉴权
每个方法都去写一段代码,冗余度太高,不利于维护,那如何做使我们的代码看起来更清爽呢?我们可以将这段代码放入拦截器去实现。
4.1、添加拦截器
Spring为我们提供了 import org.springframework.web.servlet.HandlerInterceptor 这个适配器, 实现此类,可以非常方便的实现自己的拦截器。他有三个方法:
分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲 染)、返回处理(已经渲染了页面)
- 在preHandle中,可以进行编码、安全控制等处理;
- 在postHandle中,有机会修改ModelAndView;
- 在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。
(1)创建拦截器类。创建 com.springboot.user.interceptor.JwtInterceptor
/**
* 拦截器
*/
@Component
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
private JwtUtil jwtUtil;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("经过了拦截器");
return true;
}
(2)配置拦截器类,创建com.Springboot.user.config.InterceptorConfig
/**
* 拦截器配置类
*/
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Autowired
private JwtInterceptor jwtInterceptor;
protected void addInterceptors(InterceptorRegistry registry) {
//注册拦截器要声明拦截的对象和拦截的路径
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/**/login/**");
}
}
4.2 拦截器验证token
(1)修改拦截器类 JwtInterceptor
/**
*拦截器
*/
@Component
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
private JwtUtil jwtUtil;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("经过了拦截器");
//无论如何都放行,具体能不能操作还是在具体的操作中判断;
//拦截器只是负责把请求头中有token的令牌进行解析验证;
String header = request.getHeader("Authorization");
if(header != null && header.startsWith("Bearer ")){//有头信息,且按规定的格式开头
//得到token
String token = header.substring(7);
//对令牌进行验证
try {
Claims claims = jwtUtil.parseJWT(token);
//如果是管理员
if (claims != null && "admin".equals(claims.get("roles"))){
request.setAttribute("claims_admin", claims);
}
//如果是普通用户
if (claims != null && "user".equals(claims.get("roles"))){
request.setAttribute("claims_user", claims);
}
}catch (Exception e){
throw new RuntimeException("令牌不正确");
}
}
return true;
}
}
(2)修改UserService的deleteById方法
/**
* 删除,必须有admin角色
* @param id
*/
public void deleteById(String id) {
Claims claims = (Claims) request.getAttribute("claims_admin");
if (claims == null){
throw new RuntimeException("权限不足");
}
userDao.deleteById(id);
}
5、发布信息验证Token
步骤:用户登陆签发JWT -------> 携带 token 进行相应的权限操作