【深入浅出Spring Security(五)】自定义过滤器进行前后端登录认证

这篇具有很好参考价值的文章主要介绍了【深入浅出Spring Security(五)】自定义过滤器进行前后端登录认证。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、自定义过滤器

在【深入浅出Spring Security(二)】Spring Security的实现原理 中小编阐述了默认加载的过滤器,里面有些过滤器有时并不能满足开发中的实际需求,这个时候就需要我们自定义过滤器,然后填入或者替换掉原先存在的过滤器。

首先阐述一下添加过滤器的四个方法(都是HttpSecurity中的),下面方法第一个参数都是要加入的过滤器,第二个参数是针对已存在过滤器的Class对象:

  • addFilterAt(filter,atFilter):这个相当于线性表的插入操作,把 filter 插入在 atFilter 的位置上。
  • addFilterBefore(filter,atFilter):这个是在 atFilter 前插入一个 filter 过滤器;
  • addFilterAfter(filter,atFilter):这个顾名思义是在 atFilter 后插入一个 filter 过滤器;

自定义登录认证过滤器

在 【深入浅出Spring Security(三)】默认登录认证的实现原理 中小编述说过默认的用户登录认证是走的 UsernamePasswordAuthenticationFilter,它是通过表单数据传输的方式,然后通过 request.getParameter(username/password) 的方式获取用户名密码进行认证的。但在前后端分离中,当提交登录数据给后端是 JSON 格式的话,咱就要自定义过滤器去处理这个数据获取用户名和密码进行认证了。

既然是换了一种登录认证过滤器,而且用于前后端分离,咱就可以把一些不必要的默认过滤器给pass掉。当我们调用 HttpSecurity 中的 formLogin() 方法的时候,会帮我们加载如下圈到的三个过滤器。

  1. UsernamePasswordAuthenticationFilter:负责表单认证的;
  2. DefaultLoginPageGeneratingFilter:提供登录界面的;
  3. DefaultLogoutPageGeneratingFilter:提供注销界面的;

【深入浅出Spring Security(五)】自定义过滤器进行前后端登录认证
那当我们在自定义 SecurityFilterChain 的时候不去调用 formLogin 方法的话,那这三个过滤器就相当于被我们 pass 掉了。

自定义 LoginFilter

既然咱要去实现一个 JSON 数据格式的认证,首先得先对请求进行一些判断:

  1. 请求方式应该是 POST 方式;
  2. 请求发来的数据应该是 JSON 格式的。

不满足这些的话,应该去采取默认配置的登录认证。

实现方式很简单:

咱去继承 UsernamePasswordAuthenticationFilter 重写 attemptAuthentication 方法,实现自己的认证方式即可。

代码如下:

public class LoginFilter extends UsernamePasswordAuthenticationFilter {

    public LoginFilter() {
    }

    public LoginFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {

        // 判断请求方式是否是 POST 方式
        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        // 然后判断是否是 JSON 格式的数据
        if(request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)) {
            // 如果是的话就从 JSON 中取出用户信息进行认证
            try {
                Map<String,String> userInfo = JSONObject.parseObject(request.getInputStream(), Map.class);
                String username = userInfo.get(getUsernameParameter());
                String password = userInfo.get(getPasswordParameter());
                // 封装成Authentication
                UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
                        password);
                // 仿造父类去调用AuthenticationManager.authenticate认证就可以了
                setDetails(request,authRequest);
                return getAuthenticationManager().authenticate(authRequest);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        // 否则用父类的方式去认证
        return super.attemptAuthentication(request, response);
    }
}

配置 LoginFilter

这里使用的新版本的注入方式,而不是使用继承 WebSecurityConfigurerAdapter 的方式,因为 WebSecurityConfigurerAdapter 已经被弃用了,咱还是与时俱进吧。

@EnableWebSecurity
public class SecurityConfig {

    /**
     * 构造基于内存的数据源管理
     * @return  InMemoryUserDetailsManager
     */
    @Bean
    public InMemoryUserDetailsManager inMemoryUserDetailsManager(){
        return new InMemoryUserDetailsManager(User
                .withUsername("admin")
                .password("{noop}123")
                .authorities("admin")
                .build()
        );
    }


    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .logout()
                .logoutSuccessHandler(this::onAuthenticationSuccess)
                .logoutUrl("/api/auth/logout")
                .and()
                .addFilterAt(loginFilter(http), UsernamePasswordAuthenticationFilter.class) // 注意配置 filter 哦
                .csrf()
                .disable()
                .build();
    }

    /**
     * 自定义 AuthenticationManager
     * @param http
     * @return  AuthenticationManager
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
        return http.
                getSharedObject(AuthenticationManagerBuilder.class)
                .userDetailsService(inMemoryUserDetailsManager())
                .and()
                .build();
    }

    @Bean
    public LoginFilter loginFilter(HttpSecurity http) throws Exception {
        LoginFilter loginFilter = new LoginFilter(authenticationManager(http));
        // 自定义 JSON 的 key
        loginFilter.setUsernameParameter("username");
        loginFilter.setPasswordParameter("password");
        // 自定义接收的url,默认是login
        // 此过滤器的doFilter是在AbstractAuthenticationProcessingFilter,在那里进行的url是否符合的判定
        loginFilter.setFilterProcessesUrl("/api/auth/login");
        // 设置login成功返回的JSON数据
        loginFilter.setAuthenticationSuccessHandler(this::onAuthenticationSuccess);
        // 设置login失败返回的JSON数据
        loginFilter.setAuthenticationFailureHandler(this::onAuthenticationFailure);
        return loginFilter;
    }

    @Resource
    private ObjectMapper objectMapper;
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException{
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        if(request.getRequestURI().endsWith("/login"))
            out.write(objectMapper.writeValueAsString(JsonData.success("登录成功")));
        else if(request.getRequestURI().endsWith("/logout"))
            out.write(objectMapper.writeValueAsString(JsonData.success("注销成功")));
        out.close();
    }

    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException, ServletException{
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write(objectMapper.writeValueAsString(JsonData.failure(401,exception.getMessage())));
        out.close();
    }

}

测试

【深入浅出Spring Security(五)】自定义过滤器进行前后端登录认证文章来源地址https://www.toymoban.com/news/detail-475301.html

二、总结

  1. HttpSecurity 中可以用来添加过滤器的三个方法
  • addFilterAt(filter,atFilter):这个相当于线性表的插入操作,把 filter 插入在 atFilter 的位置上。
  • addFilterBefore(filter,atFilter):这个是在 atFilter 前插入一个 filter 过滤器;
  • addFilterAfter(filter,atFilter):这个顾名思义是在 atFilter 后插入一个 filter 过滤器;
  1. 自定义登录认证可以去继承 UsernamePasswordAuthenticationFilter 过滤器重写 attemptAuthentication 方法进行自定义,然后再在自定义的 SecurityConfig 配置类里面去将它配置到 Spring 容器中,注意实例化它后一定要给它内部属性 AuthenticationManager 进行初始化赋值,不然交给Spring容器管理的时候会报错;最后添加到过滤器链中。
  2. 不去调用 formLogin 方法,那下面三个过滤器在过滤器链 SecurityFilterChain 中。
    UsernamePasswordAuthenticationFilter:负责表单认证的;
    DefaultLoginPageGeneratingFilter:提供登录界面的;
    DefaultLogoutPageGeneratingFilter:提供注销界面的;

到了这里,关于【深入浅出Spring Security(五)】自定义过滤器进行前后端登录认证的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【深入浅出 Spring Security(四)】登录用户数据的获取,超详细的源码分析

    在【深入浅出Spring Security(一)】Spring Security的整体架构 中叙述过一个SecurityContextHolder 这个类。说在处理请求时,Spring Security 会先从 Session 中取出用户登录数据,保存到 SecurityContextHolder 中,然后在请求处理完毕后,又会拿 SecurityContextHolder 中的数据保存到 Session 中,然后再

    2024年02月07日
    浏览(43)
  • 【深入浅出 Spring Security(十三)】使用 JWT 进行前后端分离认证(附源码)

    JWT 全称 Java web Token,在此所讲述的是 JWT 用于身份认证,用服务器端生成的JWT去替代原始的Session认证,以提高安全性。 JWT本质是一个Token令牌,是由三部分组成的字符串,分别是头部(header)、载荷(payload)和签名(signature)。头部一般包含该 JWT 的基本信息,例如所使用的

    2024年02月12日
    浏览(39)
  • 【深入浅出 Spring Security(十一)】授权原理分析和持久化URL权限管理

    在 【深入浅出Spring Security(一)】Spring Security的整体架构 中小编解释过授权所用的三大组件,在此再解释说明一下(三大组件具体指:ConfigAttribute、AccessDecisionManager(决策管理器)、AccessDecisionVoter(决策投票器)) ConfigAttribute 在 Spring Security 中,用户请求一个资源(通常是

    2024年02月10日
    浏览(54)
  • Spring Security内置过滤器详解

    相关文章: OAuth2的定义和运行流程 Spring Security OAuth实现Gitee快捷登录 Spring Security OAuth实现GitHub快捷登录 Spring Security的过滤器链机制 Spring Security OAuth Client配置加载源码分析 根据前面的示例,我们已经知道启动时会加载18个过滤器,并且已经知道了请求会匹配到DefaultSecurityF

    2024年02月05日
    浏览(47)
  • 【Spring Security系列】Spring Security 过滤器详解与基于JDBC的认证实现

    上文说到,Spring Security它是一个强大的和高度可定制的身份验证和访问控制框架。它提供了一套丰富的功能,用于保护基于Spring的应用程序。 上文又说到,在Spring Security中,过滤器(Filter)是一个重要的组件,用于处理身份验证、授权和其他安全相关的任务。 Spring Security 的

    2024年04月22日
    浏览(39)
  • 深入浅出Spring AOP

    第1章:引言 大家好,我是小黑,咱们今天要聊的是Java中Spring框架的AOP(面向切面编程)。对于程序员来说,理解AOP对于掌握Spring框架来说是超级关键的。它像是魔法一样,能让咱们在不改变原有代码的情况下,给程序增加各种功能。 AOP不仅仅是一个编程范式,它更是一种思

    2024年01月20日
    浏览(56)
  • 【Spring Security】使用 OncePerRequestFilter 过滤器校验登录过期、请求日志等操作

    OncePerRequestFilter 是一个过滤器,每个请求都会执行一次;一般开发中主要是做检查是否已登录、Token是否过期和授权等操作,而每个操作都是一个过滤器,下面演示一下。 检查是否登录过期过滤器 检查是否登录过期过滤器 End

    2024年02月10日
    浏览(65)
  • Spring Security 中的过滤器链是什么?它的作用是什么

    Spring Security是一个安全框架,它提供了强大的安全保护功能,可以帮助开发者更加方便地实现应用程序的安全性。Spring Security中的过滤器链是其中一个非常重要的部分,它起到了非常重要的作用。本文将介绍什么是Spring Security中的过滤器链,以及它的作用和如何使用它。同时

    2024年02月06日
    浏览(50)
  • 【深入浅出】条件概率的链式法则:定义、公式与应用

    在概率论的研究中,条件概率是一种非常重要的概念。当多个随机事件发生时,我们有时需要考虑它们同时发生的概率。条件概率的链式法则就是一种用于计算多个随机事件同时发生的概率的方法。本文将会介绍条件概率的链式法则的定义、公式以及应用。 条件概率是指在已

    2024年02月08日
    浏览(45)
  • 深入浅出 Spring:核心概念和基本用法详解

    个人主页:17_Kevin-CSDN博客 收录专栏;《Java》 在 Java 企业级应用开发中,Spring 框架已经成为了事实上的标准。它提供了一种轻量级的解决方案,使得开发者能够更轻松地构建灵活、可扩展的应用程序。在本文中,我们将探讨 Spring 框架的一些核心概念和基本用法,以此更好地

    2024年03月20日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包