编写JWT工具类
(1)在公共服务模块cloud-common的pom.xml中引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
(2)在application.yml中配置jwt的key值和有效时长
jwt:
config:
key: ghytest
ttl: 3600000
(3)创建util包,在包下创建 JWT工具类JwtUtil.java,通过@ConfigurationProperties(“jwt.config”)注解可以将application.yml中配置文件中的key值和有效时长引入,该工具类包含了生成jwt和解析jwt方法,代码如下
@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
* @param roles
* @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();
}
}
签发token
在用户成功登录系统后签发token。
(1)在用户管理模块cloud-user的pom.xml中引入公共服务模块cloud-common
<dependency>
<groupId>com.example</groupId>
<artifactId>cloud-common</artifactId>
<version>1.0.0</version>
</dependency>
(2)在启动类中配置bean
@SpringBootApplication
@EnableEurekaClient
@ComponentScan(basePackages={"com.config"})//引入swagger2
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
@Bean
public JwtUtil jwtUtil(){
return new JwtUtil();
}
}
(3)修改UserController的login方法
@RestController
@CrossOrigin
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private JwtUtil jwtUtil;
@RequestMapping(value = "/login", method = RequestMethod.POST)
public Result login(@RequestBody User user){
User userLogin = userService.login(user);
if(userLogin == null){
return Result.ResultFailed( "登录失败");
}
//生成令牌
String token = jwtUtil.createJWT(String.valueOf(userLogin.getId()), userLogin.getUserAccounts(), userRoleId);
Map<String, Object> map = new HashMap<>();
map.put("token", token);
map.put("user", userLogin);
return Result.LoginSuccess(map);
}
/**
* 根据ID查询
* @return
*/
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Result findById(@PathVariable("id") Integer id){
return Result.QuerySuccess(userService.findById(id));
}
}
(4)启动服务,验证token是否生成。
token鉴权
可以通过拦截器对token进行鉴权,Spring为我们提供了org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器,继承此类,可以非常方便的实现自己的拦截器。他有三个方法,分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面):
在预处理preHandle中,可以进行编码、安全控制等处理;
在后处理postHandle中,有机会修改ModelAndView;
在返回处理afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。
(1)在预处理preHandle中对token进行鉴权,在用户管理模块cloud-user下新建一个interceptor包,包下创建拦截器类JwtInterceptor,并在拦截器中验证token
@Component
public class JwtInterceptor extends HandlerInterceptorAdapter {
@Autowired
private JwtUtil jwtUtil;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("经过了拦截器...");
//无论如何都放行,具体能不能操作还是在具体的操作中去判断
//拦截器只是负责把请求头中包含token的令牌进行一个解析验证
String header = httpServletRequest.getHeader("Authorization");
// 如果不是映射到方法直接通过
if (!(o instanceof HandlerMethod)) {
return true;
}
// OPTIONS请求类型直接返回不处理
if ("OPTIONS".equals(httpServletRequest.getMethod())) {
return false;
}
if (header != null && !"".equals(header)) {
//如果包含有Authorization头信息,就对其进行解析
if (header.startsWith("Bearer ")) {
//得到token
String token = header.substring(7);
//对令牌进行验证
try {
Claims claims = jwtUtil.parseJWT(token);
String userId = claims.getId();
String userName = claims.getSubject();
String userRoleId = (String) claims.get("roles");
if (userId != null) {
httpServletRequest.setAttribute("user_id", userId);
}
if (userName != null) {
httpServletRequest.setAttribute("user_name", userName);
}
if (userRoleId != null) {
httpServletRequest.setAttribute("user_roleId", userRoleId);
}
} catch (Exception e) {
throw new RuntimeException("令牌不正确!");
}
}
}
return true;
}
}
(2)新建一个config包,包下创建InterceptorConfig.java,配置拦截器类
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Autowired
private JwtInterceptor jwtInterceptor;
protected void addInterceptors(InterceptorRegistry registry){
//注册拦截器要声明拦截器对象和要拦截的请求
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/**/login/**");
}
}
(3)启动服务,验证拦截器是否生效。
在微服务中,如果在每个服务中都定义相应的拦截器对token进行验证,这样会让我们重复写很多代码,我们可以将对token的鉴权写在网关的前置拦截器中。