项目集成 Spring Security 携带 token 访问接口(三)

404 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

前言

上一篇文章,我们学习了如何通过接口的方式进行登录获取 token,此篇文章我们来学习通过拦截器来过滤请求,不在放行接口且 token 无效的进行拦截并返回 401.

未命名文件.png

试想一下,我们判断 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 请求的示例:

image.png

可以看到存在两个部分,以Bearer开头,空格后跟我们登录获取到的 token 。

为什么是 Bearer 开头?

还是上面的说法,一种规范,你也可以自定义。

把过滤器添加到 spring security

在文件 SecurityConfiguration.java 中增加:

@Resource
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

filterChain 方法中把过滤器加进去:

image.png

.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

postman 测试

把登录接口获取的 token 放在这个 bearar 的 token 中即可访问之前 401 的资源

image.png

总结

  1. 增加过滤器,编写鉴权逻辑
  2. 在 spring security 中添加过滤器
  3. 接口在请求头携带 token 访问需鉴权资源