1. 环境配置
pom.xml
<!--jwt 依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!--数据库 和 JPA 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
application.yml
server:
port: 9008 # 端口号 (与jwt无关)
spring:
application:
name: sunmone-user #指定服务名 (与jwt无关)
datasource: # 数据库配置(与jwt无关)
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/数据库名称?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root
jpa: # 使用JPA访问数据库(与jwt无关)
database: MySQL
show-sql: true
jwt: # jwt 自定义参数
config:
key: sunmone # 盐
ttl: 3600000 # token过期时间 一小时
JWT 工具类
package utils;
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") // 在 application.yml 中取 key 和 ttl的值
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);
// 创建 token 令牌
JwtBuilder builder = Jwts.builder().setId(id)
.setSubject(subject) // jwt 指定的用户
.setIssuedAt(now) // jwt 签发时间
.signWith(SignatureAlgorithm.HS256, key).claim("roles", roles); //jwt 头部 和自定义角色权限
// 如果 ttl大于0 设置token令牌过期时间
if (ttl > 0) {
builder.setExpiration( new Date( nowMillis + ttl));
}
// 返回 token 令牌
return builder.compact();
}
/**
* 解析JWT
* @param jwtStr
* @return
*/
public Claims parseJWT(String jwtStr){
// 解析令牌
return Jwts.parser()
.setSigningKey(key) // 令牌的 盐
.parseClaimsJws(jwtStr) // token 值
.getBody();
}
}
Application 启动类
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
// 创建 JWT Utils对象
@Bean
public JwtUtil jwtUtil() {
return new JwtUtil();
}
}
2. JWT配置
如果我们每个方法都去写一段代码,冗余度太高,不利于维护,那如何做使我们的代码看起来更清爽呢?我们可以将这段代码放入拦截器去实现
拦截器和过滤器放在同一目录下即可
2.1 过滤器
import utils.JwtUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* JWT 拦截器
* 在preHandle中,可以进行编码、安全控制等处理;前置拦截
* 在postHandle中,有机会修改ModelAndView; 中间拦截
* 在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。 异常拦截
*/
@Component // 把拦截器添加到Spring中
public class JwtFilter extends HandlerInterceptorAdapter {
@Autowired
private JwtUtil jwtUtil;
/***
* 重写 JWT前置拦截器,所有请求都会被拦截 所以要写一个extends WebMvcConfigurationSupport设置拦击规则
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// JWT 前置拦截器
System.out.println("经过JwtFilter");
// 前后端约定:前端请求服务时需要添加头信息Authorization ,内容为Bearer+空格+token
// 获取 token 令牌
String Authorization = request.getHeader("Authorization");
// 如果 请求头不为空
if (Authorization != null) {
// 如果 参数中以 Bearer空格为开头
if (Authorization.startsWith("Bearer ")) {
// 取 token数据
if (Authorization.length() > 8){
String token = Authorization.substring(7);
// 解析令牌获取 Claims对象
Claims claims = jwtUtil.parseJWT(token);//获取载荷
// 如果Claims不为空
if (claims != null) {
// 判断权限 加入到请求参数中
if (claims.get("roles").equals("admin")) {//管理员身份
request.setAttribute("admin_claims", claims);
}
// 判断权限 加入到请求参数中
if (claims.get("roles").equals("user")) {//普通用户
request.setAttribute("user_claims", claims);
}
}
}
}
}
// 始终放行 由具体资源来决定结果
return true;
}
}
2.2 拦截器
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
/**
* 拦截器配置
*/
@Configuration // 配置类 继承 extends WebMvcConfigurationSupport
public class ApplicationConfig extends WebMvcConfigurationSupport {
// 加载 jwt拦截器
@Autowired
private JwtFilter jwtFilter;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 加载过滤器 除了login方法其余全部拦截
registry.addInterceptor(jwtFilter)
.addPathPatterns("/**")
.excludePathPatterns("/**/login");
}
}
3. Controller层鉴权
我们使用了前置拦截器,所有请求经过都会被拦截,除了login请求,那么只需要在Controller层把参数获取,判断用户就可以进行权限鉴定了
@RestController
@CrossOrigin
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private HttpServletRequest request;
@Autowired
private JwtUtil jwtUtil;
/**
* 删除
*
* @param id
*/
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public Result delete(@PathVariable String id) {
// 有前置拦截器 只需要获取 token参数即可
Claims claims = (Claims) request.getAttribute("admin_claims");
if (claims == null){
return new Result(false, StatusCode.ACCESSERROR, "权限不足");
}
userService.deleteById(id);
return new Result(true, StatusCode.OK, "删除成功");
}
}