SpringBoot实现用户登录拦截功能
使用JWT实现登录拦截的实现方式如下:
- 创建一个工具类,实现生成token(密匙)、token(密匙)认证功能
- 创建一个过滤器,对请求进行过滤,满足要求的请求才放行
在实现之前需要对项目添加依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
工具类的实现
工具类主要提供两个方法:
- 生成token(密匙):依托JWT提供的方法实现
- token认证:对传入的token进行验证(加密以及时效性),并验证用户信息是否正确
工具类代码如下:
1.生成token
/**
* 登录后生成token
* JWT_EXPIRE_TIME 是需要配置的Token过期时间,根据自己系统需要进行设置
* JWT_KEY 是需要配置的加密串(相当于密码),需自定义
* @param userId 用户id
* @param userName 用户姓名
* @return 返回生成的token
*/
public String createToken(String userId,String userName){
Map<String,Object> header = new HashMap<>();
header.put("typ","JWT");
header.put("alg","HS256");
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.MILLISECOND, JWT_EXPIRE_TIME);
//setID:用户ID
//setExpiration:token过期时间 当前时间+有效时间
//setSubject:用户名
//setIssuedAt:token创建时间
//signWith:加密方式
JwtBuilder builder = Jwts.builder().setHeader(header)
.setId(userId)
.setExpiration(calendar.getTime())
.setSubject(userName)
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256,JWT_KEY);
return builder.compact();
}
2.验证token,其中BaseUserDDTO、BaseContext、BaseContextHolder、baseUserDAO、BaseException都是作者编写的项目中的变量,主要是验证通过后将用户信息保存到上线文环境中,供通过验证后进入的实际方法中的代码使用。
/**
* 验证token是否有效
* @param token 请求头中携带的token
* @return token验证结果 false认证失败
*/
public boolean verify(String token) {
Claims claims;
try {
//token过期后,会抛出ExpiredJwtException 异常,通过这个来判定token过期,
claims = Jwts.parser().setSigningKey(JWT_KEY).parseClaimsJws(token).getBody();
} catch (ExpiredJwtException e){
throw new BaseException(ErrorInfoConst.SYS_AUTH_TOKEN_OVERTIME);
}
//从token中获取用户id,查询该Id的用户是否存在,存在则token验证通过
String id = claims.getId();
// 查询登录用户信息
BaseUserDDTO baseUserDDTO = jwtUtils.baseUserDAO.selectById(id);
if (baseUserDDTO == null || baseUserDDTO.getUserId() == null) {
throw new BaseException(ErrorInfoConst.SYS_AUTH_TOKEN_NOCURUSER);
}
BaseContext baseContext = BaseContextHolder.getContext();
// 将查询出的数据放入上下文环境
BaseCurrentUser baseCurrentUser = new BaseCurrentUser();
baseCurrentUser.setUserId(baseUserDDTO.getUserId());
baseCurrentUser.setUserName(baseUserDDTO.getUserName());
baseCurrentUser.setOrgId(baseUserDDTO.getOrgId());
baseCurrentUser.setOrgName(baseUserDDTO.getOrgName());
baseCurrentUser.setUserLevel(baseUserDDTO.getUserLevel());
baseCurrentUser.setUserType(baseUserDDTO.getUserType());
baseContext.setBaseCurrentUser(baseCurrentUser);
BaseContextHolder.setContext(baseContext);
return true;
}
过滤器的实现
过滤器的实现需要编写一个类实现HandlerInterceptor并注册到Spring上。代码如下:
// 通过注解注册到Spring中
@Component
public class AuthVerifyInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(AuthVerifyInterceptor.class);
// 上一步中定义的工具类
JwtUtils jwtUtils = new JwtUtils();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 这里实现校验
// 跨域请求会首先发一个option请求,直接返回正常状态并通过拦截器
if("OPTIONS".equals(request.getMethod())){
response.setStatus(HttpServletResponse.SC_OK);
return true;
}
response.setCharacterEncoding("utf-8");
// 获取请求中携带的token
if (request.getCookies() != null && request.getCookies()[0] != null) {
Cookie[] cookies = request.getCookies();
String token = null;
for (Cookie item: cookies) {
if ("TOKEN".equals(item.getName())) {
token = item.getValue();
}
}
if (jwtUtils.verify(token)) {
// 更新token
BaseCurrentUser baseCurrentUser = BaseContextHolder.getContext().getBaseCurrentUser();
token = jwtUtils.createToken(baseCurrentUser.getUserId(), baseCurrentUser.getUserName());
if (result) {
Cookie cookie = new Cookie("TOKEN", token);
// 服务的地址,返回给前台保存Token使用的
cookie.setDomain("127.0.0.1");
cookie.setHttpOnly(false);
cookie.setPath("/");
response.addCookie(cookie);
return true;
}
}
}
return false;
}
}
最后只需要在实现登录功能的代码中加上生成并返回Token的代码,就能实现登录拦截功能了。
设置白名单
有些时候有些地址不需要拦截,可以设置白名单,方式如下。
创建一个配置类,实现WebMvcConfigurer即可:
@Configuration
public class AuthVerifyConfiguration implements WebMvcConfigurer {
// 刚刚实现的登录拦截类
@Autowired
protected AuthVerifyInterceptor authVerifyInterceptor;
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("*")
.allowedMethods("*")
.allowedOriginPatterns("*")
.allowCredentials(true);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
List<String> excludePath = new ArrayList<>();
//排除拦截,除了注册登录(此时还没token),其他都拦截
excludePath.add("/web/auth/**"); //登录和注册
excludePath.add("/doc.html"); //swagger
excludePath.add("/swagger-ui.html"); //swagger
excludePath.add("/swagger-ui.html#"); //swagger
excludePath.add("/swagger-ui.html#/**"); //swagger
excludePath.add("/webjars/**"); //swagger
excludePath.add("/web/codegenerator/*");
excludePath.add("/swagger-resources/**"); //swagger
excludePath.add("/v2/api-docs"); //swagger
registry.addInterceptor(authVerifyInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(excludePath);
WebMvcConfigurer.super.addInterceptors(registry);
}
}