首页 > 其他分享 >Spring Security 5.7.* 没有WebSecurityConfigurerAdapter如何配置AuthenticationManager

Spring Security 5.7.* 没有WebSecurityConfigurerAdapter如何配置AuthenticationManager

时间:2022-11-29 14:58:57浏览次数:53  
标签:authenticationManager 5.7 Spring AuthenticationManager loginFilter new class Log

简述

我用了Spring security 5.7.4这个比较新的版本,而且官方已经标注说明WebSecurityConfigurerAdapter已经过期,那么我就根据官方新的配置方式进行了配置,就在我自定义LoginFilter这个类去继承UsernamePasswordAuthenticationFilter且覆盖attemptAuthentication方法时需要配置AuthenticationManager我就懵了,旧的方式是继承WebSecurityConfigurerAdapter且覆盖authenticationManagerBean()方法去调用父类方法,现在没了WebSecurityConfigurerAdapter,新的配置方式怎么配呢?然后就在这个坑徘徊了许久,所以现在我把我的解决办法记录一下,以此让更多的伙伴避免这个坑。

WebSecurityConfigurerAdapter过期的官方博客说明新用法传送门

考虑到博客的篇幅,下面我粘贴主要的代码和逻辑。

旧的配置方式

下面代码我配置了接收前端JSON的形式,而且配置了验证码的校验:

public class LoginFilter extends UsernamePasswordAuthenticationFilter {
    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());
        }
        HttpSession session = request.getSession();
        // 获得 session 中的 验证码值
        String sessionVerifyCode = (String) session.getAttribute("verify_code");
        // 判断请求格式是否是 JSON
        if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
            Map<String, String> loginData = new HashMap<>();
            try {
                loginData = new ObjectMapper().readValue(request.getInputStream(), Map.class);
            } catch (IOException e) {
            } finally {
                String code = loginData.get("code");
                checkVerifyCode(sessionVerifyCode, code);
            }
            String username = loginData.get(getUsernameParameter());
            String password = loginData.get(getPasswordParameter());
            if (StringUtils.isEmpty(username)) {
                throw new AuthenticationServiceException("用户名不能为空");
            }
            if (StringUtils.isEmpty(password)) {
                throw new AuthenticationServiceException("密码不能为空");
            }
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                    username, password);
            setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        } else {
            checkVerifyCode(sessionVerifyCode, request.getParameter("code"));
            return super.attemptAuthentication(request, response);
        }
    }

    private void checkVerifyCode(String sessionVerifyCode, String code) {
        if (StringUtils.isEmpty(code)) {
            throw new AuthenticationServiceException("验证码不能为空!");
        }
        if (StringUtils.isEmpty(sessionVerifyCode)) {
            throw new AuthenticationServiceException("请重新申请验证码!");
        }
        if (!sessionVerifyCode.equalsIgnoreCase(code)) {
            throw new AuthenticationServiceException("验证码错误!");
        }
    }
}

下面代码WebSecurityConfigurerAdapter这个类的配置,观察authenticationManagerBean()这个方法就是调用了父类的方法:


    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean()
            throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 用自定义的 LoginFilter 实例代替 UsernamePasswordAuthenticationFilter
        http.addFilterBefore(loginFilter(), UsernamePasswordAuthenticationFilter.class);

        http.authorizeRequests()  //开启配置
                // 验证码、登录接口放行
                .antMatchers("/verify-code","/sso/login").permitAll()
                .anyRequest() //其他请求
                .authenticated().and()//验证   表示其他请求需要登录才能访问
                .csrf().disable();  // 禁用 csrf 保护
                
                http.exceptionHandling().authenticationEntryPoint(new MyAuthenticationEntryPoint());
    }

    @Bean
    LoginFilter loginFilter() throws Exception {
        LoginFilter loginFilter = new LoginFilter();
        loginFilter.setFilterProcessesUrl("/sso/login");
        loginFilter.setUsernameParameter("username");
        loginFilter.setPasswordParameter("password");
        loginFilter.setAuthenticationManager(authenticationManagerBean());
        loginFilter.setAuthenticationSuccessHandler(new MyAuthenticationSuccessHandler());
        loginFilter.setAuthenticationFailureHandler(new MyAuthenticationFailureHandler());
        return loginFilter;
    }

上面代码是有WebSecurityConfigurerAdapter这个类的情况,下面看没有WebSecurityConfigurerAdapter这个类是怎么配的。

新的配置方式

新的配置方式,这里有两种方法:

  • 配置全局的AuthenticationManager
  • 自定义一个Configurer的方式

配置全局的AuthenticationManager的方式:

step 1: 上面粘贴的LoginFilter的代码

这里得留意我在LoginFilter以构造方法来传递给父类,当然你也可以设置为setter的方式,个人爱好。

step 2: 在配置类中配置以下代码


	@Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
                .authorizeRequests();
        //不需要保护的资源路径允许访问
        for (String url : ignoreUrlsConfig.getUrls()) {
            registry.antMatchers(url).permitAll();
        }
        //允许跨域请求的OPTIONS请求
        registry.antMatchers(HttpMethod.OPTIONS)
                .permitAll();
        // 任何请求需要身份认证
        registry.and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                // 关闭跨站请求防护及不使用session
                .and()
                .csrf()
                .disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                // 自定义权限拒绝处理类
                .and()
                .exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler)
                .authenticationEntryPoint(restAuthenticationEntryPoint)
                // 自定义权限拦截器JWT过滤器
                .and()
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
                .addFilterAt(loginFilter(authenticationManager(httpSecurity.getSharedObject(AuthenticationConfiguration.class))), UsernamePasswordAuthenticationFilter.class);
        //有动态权限配置时添加动态权限校验过滤器
        if(dynamicSecurityService!=null){
            registry.and().addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
        }
        return httpSecurity.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }


    LoginFilter loginFilter(AuthenticationManager authenticationManager) throws Exception {
        LoginFilter loginFilter = new LoginFilter(authenticationManager);
        loginFilter.setFilterProcessesUrl("/login");
        loginFilter.setUsernameParameter("username");
        loginFilter.setPasswordParameter("password");
//        loginFilter.setAuthenticationManager();
        loginFilter.setAuthenticationSuccessHandler(new MyAuthenticationSuccessHandler());
        loginFilter.setAuthenticationFailureHandler(new MyAuthenticationFailureHandler());
        return loginFilter;
    }

留意上面.addFilterAt(loginFilter(authenticationManager(httpSecurity.getSharedObject(AuthenticationConfiguration.class))), UsernamePasswordAuthenticationFilter.class);这行代码

自定义一个Configurer的方式

step 1: 跟上面LoginFilter一样的代码

step 2: 新建一个Configurer类,代码如下:

public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
        http.addFilter(loginFilter(authenticationManager));
    }

    public static MyCustomDsl customDsl() {
        return new MyCustomDsl();
    }

    LoginFilter loginFilter(AuthenticationManager authenticationManager) throws Exception {
        LoginFilter loginFilter = new LoginFilter(authenticationManager);
        loginFilter.setFilterProcessesUrl("/login");
        loginFilter.setUsernameParameter("username");
        loginFilter.setPasswordParameter("password");
//        loginFilter.setAuthenticationManager(authenticationManager);
        loginFilter.setAuthenticationSuccessHandler(new MyAuthenticationSuccessHandler());
        loginFilter.setAuthenticationFailureHandler(new MyAuthenticationFailureHandler());
        return loginFilter;
    }
}

step 3: 参考上面的配置类的@Bean SecurityFilterChain filterChain(HttpSecurity httpSecurity)的代码,去掉.addFilterAt(loginFilter(authenticationManager(httpSecurity.getSharedObject(AuthenticationConfiguration.class))), UsernamePasswordAuthenticationFilter.class);这行代码,新添.and().apply(MyCustomDsl.customDsl())这行代码,详细如下:

@Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
                .authorizeRequests();
        ...
		...
        // 任何请求需要身份认证
        registry.and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
				.and()
				.apply(MyCustomDsl.customDsl())
		...
		...

总结

根据自己的实际情况参考以上代码,如果还是解决不了的话,可以参考我下面的参考连接或许给你提供新思路,实在解决不了那就去搜索引擎找了,
推荐google、stackoverflow。最后提一下,下面的参考连接有官方的说明,点进去看看吧!

参考连接:

stackoverflow
spring security官方文档
spring security github issues

标签:authenticationManager,5.7,Spring,AuthenticationManager,loginFilter,new,class,Log
From: https://www.cnblogs.com/tiger-yam/p/16935369.html

相关文章

  • springboot的启动流程
           ......
  • Spring+struts+ibatis(一)环境准备工作
    首先我们先了解几个jar包的作用和一些未曾见过的接口和类xwork-2.0.7.jarXWork是一个标准的Command模式实现,并且完全从web层脱离出来。Xwork提供了很多核心功能:前端拦截机(in......
  • 源码、二进制安装MySQL5.7.39
    源码、二进制安装MySQL5.7.391.源码安装源码安装包下载链接https://downloads.mysql.com/archives/get/p/23/file/mysql-5.7.39.tar.gz1.1安装依赖包yum-yinstallw......
  • spring mvc获取路径参数的几种方式
    springmvc获取路径参数的几种方式 SpringMVC是一个基于DispatcherServlet的MVC框架,每一个请求最先访问的都是DispatcherServlet,DispatcherServlet负责转发每一个Request......
  • Spring mvc 返回json格式 - 龙企阁
    第一次使用springmvc,在此也算是记录一下以防忘记,希望有经验的朋友指出不足的地方一、使用maven管理jar。<dependency><groupId>org.codehaus.jackson</groupId><artif......
  • spring中依赖注入与aop讲解
    一、依赖注入这个属于IOC依赖注入,也叫控制反转,IOC是说类的实例由容器产生,而不是我们用new的方式创建实例,控制端发生了改变所以叫控制反转。<?xmlversion="1.0"encoding="U......
  • SpringCloud Alibaba(四) - Nacos 配置中心
    1、环境搭建1.1依赖<!--nacos注册中心注解@EnableDiscoveryClient--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-......
  • SpringMVC异常处理
    异常处理方式* 配置简单异常处理器SimpleMappingExceptionResolver* 配置自定义异常处理器自定义异常处理步骤* 创建异常处理器实现HandlerExceptio......
  • SpringBoot 实际项目开发中工厂模式的巧妙使用
    简单工厂模式:     简单工厂模式是创建型模式,创建型模式顾名思义,也就是说在创建对象的时候,遇到了瓶颈才会选择的设计模式。那么该什么情况使用呢。  简单工厂模式......
  • Spring Boot测试
    SpringBoot测试一、了解单元测试单元测试(UnitTest)是为了检验程序的正确性。一个单元可能是单个程序、类、对象、方法等,它是应用程序的最小可测试部件。单元测试的必......