SpringBoot前后端分离项目使用SpringSecurity+JWT做权限认证,修改自定义认证过滤器的访问路径

写在前面:

这个问题在网上一直找不到我想要的答案,是通过我自己不断试错最后才成功的,故写出来分享一下。这是我的第一篇分享博客,如果有哪里写的不好的地方,请多多见谅!

需求提出:

SpringSecurity自带的权限认证有很多种,有Form Login的形式,也有http basic的形式等等。我们之前的JSP项目是通过<form>标签表单提交来进行登录认证的,这时候使用SpringSecurity的Form Login认证是最适合的,并且我们也可以在配置类中可以使用HttpSecurity对象做一些自定义的配置(登录处理器,username参数名,password参数名等等),如下代码所示:

http.formLogin()
            .loginProcessingUrl("/user/login")
            .usernameParameter("account")
            .passwordParameter("password")
            .permitAll();

通过几行简简单单的配置我们就可以将用户认证处理器url修改成/user/login了。

但是在前后端分离的项目中,如果我们要实现登录认证,就要使用Token来保存用户信息,并且要自定义一个用于登录认证的过滤器,继承UsernamePasswordAuthenticationFilter这个类,重写SpringSecurity的认证逻辑。我用到的是JWT来生成Token,这里贴出我的认证代码:

/**
 * 登录验证过滤器(只拦截管理员和商家的登录请求)
 *
 * @author Ambitious
 * @date 2022/3/17 22:45
 */
@Slf4j
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {

    private final RsaKeys rsaKeys;
    private final AuthenticationManager authenticationManager;

    public JwtLoginFilter(AuthenticationManager manager, RsaKeys rsaKeys) {
        this.authenticationManager = manager;
        this.rsaKeys = rsaKeys;
    }

    /**
     * 获取用户的登录信息(账号,密码),并进行校验
     * @param request 请求对象
     * @param response 响应对象
     * @return 认证管理器的认证结果
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        log.info("调用了自定义的登录认证过滤器");
        // 获取登录参数
        String account = request.getParameter("account");
        String password = request.getParameter("password");
        if(!StringUtils.hasText(account) || !StringUtils.hasText(password)) {
            // 参数不合法
            ResponseUtils.write(response, ResponseVO.fail(SysCode.UN_VALID_REQUEST_PARAMETER));
            return null;
        }
        // 构造SpringSecurity登录token
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(account, password);
        return this.authenticationManager.authenticate(token);
    }

    /**
     * 用户认证成功,使用JWT生成Token返回
     * @param request 请求对象
     * @param response 响应对象
     * @param chain 过滤器链
     * @param authResult 认证结果
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        // 获取认证用户对象,去除敏感信息
        User user = (User) authResult.getPrincipal();
        user.setPassword(null);
        // 生成Token
        String authorization = JwtUtils.generateToken(user, rsaKeys.getPrivateKey());
        // 存入响应头并返回
        response.setHeader("Authorization", "Bearer " + authorization);
        ResponseUtils.write(response, ResponseVO.ok(SysCode.LOGIN_SUCCESS));
    }

    /**
     * 认证失败
     * @param request 请求对象
     * @param response 响应对象
     * @param failed 失败原因
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        ResponseUtils.write(response, ResponseVO.fail(SysCode.BAD_AUTHENTICATE));
    }
}

 这样子配置之后,再在SpringSecurity配置类中使用http.addFilter加入我们自定义的这个过滤器就可以通过请求http://localhost:8080/login接口并且携带登录参数来进行登录认证了,认证成功之后会将Token设置在响应头中,然后前端拿到Token进行其他的请求。

 那么,如果我不想要这个默认接口呢?我的接口规范就是要/api/**,直接使用/login的话有可能会给项目后期部署带来不必要的麻烦。

尝试解决这个问题

1. 老办法解决

最开始的时候,我觉得这个需求很容易实现,就是继续使用http.formLogin方法,配置loginProcessUrl为/api/user/login就可以达到我的目的了。可结果是什么呢:

 它给我跳到了SpringSecurity内置的登录页面!!!!

 后来经过分析我发现,只要我配置了http.formLogin这个规则,我自定义的过滤器就失效了。。它进了默认的UsernamePasswordAuthenticationFilter,并且我的登录用户参数名是account不是默认的username,导致在数据库中查找不到用户数据,这时就认定我认证失败,所以给我跳到了登录页面。。。。

2. 寻求百度解决

令我没有想到的是这么简单的一个需求,居然在百度找不到一篇文章能给出解决办法,,

不过在我耐心的修改搜索关键词之后,我还是找到了一篇比较接近我这个需求的办法,它说可以给自定义的这个过滤器设置一个filterProcessUrl就可以了,看这属性名,它不就是我要的东西么,我只要给自定义的过滤器设置一个路径就可以让登录请求进到这个过滤器了。于是还是在刚刚那个JwtLoginFIlter类中,我加入了以下方法:

@Override
public void setFilterProcessesUrl(String filterProcessesUrl) {
    setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/user/login"));
}

这个方法的代码我参考了父类的写法,将自己想要的url配置进去

天真的我以为这样就能解决问题了,可结果是,这个方法它根本就没有被调用!

问题解决

最后我发现,如果在JwtLoginFilter这个类中重写过滤器的doFilter方法,是会被调用的!那么我可以在doFilter中设置我想要的这个url,然后再继续执行过滤器链,这样Spring内部就会修改默认的认证处理器url,从而调用我自定义过滤器的attemptAutentication方法,走自己的认证逻辑,问题成功解决!

JwtLoginFilter完整代码如下:

@Slf4j
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {

    private final RsaKeys rsaKeys;
    private final AuthenticationManager authenticationManager;

    public JwtLoginFilter(AuthenticationManager manager, RsaKeys rsaKeys) {
        this.authenticationManager = manager;
        this.rsaKeys = rsaKeys;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("进来自定义的过滤器了");
        setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/user/login"));
        super.doFilter(request, response, chain);
    }

    /**
     * 获取用户的登录信息(账号,密码),并进行校验
     * @param request 请求对象
     * @param response 响应对象
     * @return 认证管理器的认证结果
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        log.info("调用了自定义的登录认证过滤器");
        // 获取登录参数
        String account = request.getParameter("account");
        String password = request.getParameter("password");
        if(!StringUtils.hasText(account) || !StringUtils.hasText(password)) {
            // 参数不合法
            ResponseUtils.write(response, ResponseVO.fail(SysCode.UN_VALID_REQUEST_PARAMETER));
            return null;
        }
        // 构造SpringSecurity登录token
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(account, password);
        return this.authenticationManager.authenticate(token);
    }

    /**
     * 用户认证成功,使用JWT生成Token返回
     * @param request 请求对象
     * @param response 响应对象
     * @param chain 过滤器链
     * @param authResult 认证结果
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        // 获取认证用户对象,去除敏感信息
        User user = (User) authResult.getPrincipal();
        user.setPassword(null);
        // 生成Token
        String authorization = JwtUtils.generateToken(user, rsaKeys.getPrivateKey());
        // 存入响应头并返回
        response.setHeader("Authorization", "Bearer " + authorization);
        ResponseUtils.write(response, ResponseVO.ok(SysCode.LOGIN_SUCCESS));
    }

    /**
     * 认证失败
     * @param request 请求对象
     * @param response 响应对象
     * @param failed 失败原因
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        ResponseUtils.write(response, ResponseVO.fail(SysCode.BAD_AUTHENTICATE));
    }
}

在SpringSecurity配置类中,无需添加http.formLogin配置,只要配置自定义过滤器即可

@Override
protected void configure(HttpSecurity http) throws Exception {
    // 放行所有接口,由注解进行限制
    http.authorizeRequests()
            .antMatchers("/**").permitAll();
    // 添加自定义的过滤器
    http.addFilter(new JwtLoginFilter(authenticationManager(), rsaKeys))
            .addFilter(new JwtVerifyTokenFilter(authenticationManager(), rsaKeys));
    // csrf拦截
    http.csrf().disable();
}

 

 以上就是本篇文章的所有内容了,希望对你有帮助!


我的 个人博客 上线啦,欢迎到访~

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
Spring Boot和Vue.js是两个非常流行的技术栈,可以非常好地实现前后端分离的开发模式。SecurityJWT是两个很好的工具,可以帮助我们实现安全的登录和授权机制。 以下是实现Spring Boot和Vue.js前后端分离的步骤: 1.创建Spring Boot工程 首先,我们需要创建一个Spring Boot工程,可以使用Spring Initializr来生成一个基本的Maven项目,添加所需的依赖项,包括Spring SecurityJWT。 2.配置Spring SecuritySpring Security中,我们需要定义一个安全配置类,该类将定义我们的安全策略和JWT的配置。在这里,我们可以使用注解来定义我们的安全策略,如@PreAuthorize和@Secured。 3.实现JWT JWT是一种基于令牌的身份验证机制,它使用JSON Web Token来传递安全信息。在我们的应用程序中,我们需要实现JWT的生成和验证机制,以便我们可以安全地登录和授权。 4.配置Vue.js 在Vue.js中,我们需要创建一个Vue.js项目,并使用Vue CLI来安装和配置我们的项目。我们需要使用Vue Router来定义我们的路由,并使用Axios来发送HTTP请求。 5.实现登录和授权 最后,我们需要实现登录和授权机制,以便用户可以安全地登录和访问我们的应用程序。在Vue.js中,我们可以使用Vue Router和Axios来发送HTTP请求,并在Spring Boot中使用JWT来验证用户身份。 总结 以上是实现Spring Boot和Vue.js前后端分离的步骤,我们可以使用SecurityJWT实现安全的登录和授权机制。这种开发模式可以让我们更好地实现前后端分离,提高我们的开发效率和应用程序的安全性。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AmbitiousJun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值