携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
前言
上一篇文章,我们学习了如何通过接口的方式进行登录获取 token,此篇文章我们来学习通过拦截器来过滤请求,不在放行接口且 token 无效的进行拦截并返回 401.
试想一下,我们判断 token 权限,是在 Controller 层设置吗?
也不是不可以,但是要写很多重复的,这就不符合分模块的思想。写业务的时候不应该考虑权限,因此我们可以采用过滤器,统一做鉴权处理。
新建过滤器
文件: JwtAuthenticationTokenFilter.java
package com.example.auth.filter;
import com.example.auth.util.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
private UserDetailsService userDetailsService;
private JwtTokenUtil jwtTokenUtil;
@Autowired
private void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Autowired
public void setJwtTokenUtil(JwtTokenUtil jwtTokenUtil) {
this.jwtTokenUtil = jwtTokenUtil;
}
private static final String TOKEN_HEADER = "Authorization";
private static final String TOKEN_START = "Bearer ";
@Override
public final void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String authHeader = request.getHeader(TOKEN_HEADER);
// 存在token
if (StringUtils.hasText(authHeader) && authHeader.startsWith(TOKEN_START)) {
String authToken = authHeader.substring(TOKEN_START.length());
String username = jwtTokenUtil.extractUsername(authToken);
// 存在token 但是用户名未登录
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// 登录
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// 验证token是否有效,重新设置用户对象
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
// 重新设置回用户对象
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
}
// 放行
chain.doFilter(request, response);
}
}
讲解:
private static final String TOKEN_HEADER = "Authorization"
这里的是请求头的 key,也可以自定义,目前这个是业界统一的标识,就是这样写,其他人就能立马知道,这是用什么框架写的,比较好交接
看一下目前携带 token 请求的示例:
可以看到存在两个部分,以Bearer开头,空格后跟我们登录获取到的 token 。
为什么是 Bearer 开头?
还是上面的说法,一种规范,你也可以自定义。
把过滤器添加到 spring security
在文件 SecurityConfiguration.java 中增加:
@Resource
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
在 filterChain 方法中把过滤器加进去:
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
postman 测试
把登录接口获取的 token 放在这个 bearar 的 token 中即可访问之前 401 的资源
总结
- 增加过滤器,编写鉴权逻辑
- 在 spring security 中添加过滤器
- 接口在请求头携带 token 访问需鉴权资源