SpringSecurity6 Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exce

一.
项目自springsecurity5升级到6 配置类修改。其中突出的问题是认证管理器的变化。
如下图报错:
ervlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Filter execution threw an exception] with root cause

at jdk.proxy3/jdk.proxy3.$Proxy144.authenticate(Unknown Source)
在这里插入图片描述
在这里插入图片描述

初始化开始:
1.
在这里插入图片描述
2.初始化没找到认证管理实例走到懒加载
在这里插入图片描述
3.他还是加载他自己,循环引用了在这里插入图片描述

2.解决方案

springsecurity6的认证管理Bean方法被设置为私有方法。因此无法以以下形式进行:

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

而应当使用以下配置,ss 配置类

/**
 * @Description: spring security 配置类
 */
@Configuration //配置类
// 开启注解 @PreAuthorize @PostAuthorize @Secured
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig {

    //    // 授权处理器
//    @Resource
//    private AccessDeniedHandlerImpl accessDeniedHandler;
    // jwt过滤器
    @Resource
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    // 手机号短信登录
    @Lazy
    @Resource
    private SmsCodeAuthenticationProvider smsCodeAuthenticationProvider;
    // 手机号账号登录
    @Lazy
    @Resource
    private MobileAccountAuthenticationProvider mobileAccountAuthenticationProvider;
    // 微信登录
    @Lazy
    @Resource
    private WeChatAuthenticationProvider weChatAuthenticationProvider;
    @Resource
    private AuthenticationManagerProcessingFilter authenticationManagerProcessingFilter;



    /**
     * 获取AuthenticationManager(认证管理器),登录时认证使用
     */
    @Bean
    public AuthenticationManager authenticationManager(List<AuthenticationProvider> myAuthenticationProviders) {
        myAuthenticationProviders.addAll(List.of(smsCodeAuthenticationProvider, mobileAccountAuthenticationProvider, weChatAuthenticationProvider));
        return new ProviderManager(myAuthenticationProviders);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    /**
     * 跨域配置
     *
     * @return corsConfigurationSource
     */
//    private CorsConfigurationSource corsConfigurationSource() {
//        CorsConfiguration corsConfiguration = new CorsConfiguration();
//        corsConfiguration.setAllowedHeaders(List.of("*"));
//        //允许从任意站点跨域
//        corsConfiguration.setAllowedOrigins(List.of("*"));
//        corsConfiguration.setAllowedMethods(List.of("*"));
//        // 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
//        corsConfiguration.setMaxAge(3600L);
//        // 允许携带凭证
//        corsConfiguration.setAllowCredentials(true);
//        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
//        // 对所有请求都生效
//        source.registerCorsConfiguration("/**", corsConfiguration);
//        return source;
//    }

    /**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().requestMatchers("/**");
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .sessionManagement(AbstractHttpConfigurer::disable)
//                .cors(cros -> cros.configurationSource(corsConfigurationSource()))
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/test/**",
                                "/member/artist/v1/getArtist/**",
                                "/member/artist/v1/search/**",
                                "/member/artist/v1/artist/**",
                                "/member/artist/v1/list/**",
                                "/member/province-university/**",
                                "/member/user/v1/list/**").permitAll()
                        .requestMatchers("/music-score/score/v1/list",
                                "/music-score/score/v1/search/**",
                                "/music-score/score/v1/popular/**",
                                "/music-score/score/v1/info/**",
                                "/music-score/score/v1/score-file/**",
                                "/music-score/score/v1/artist/score",
                                "/music-score/score/v1/score-collect/**",
                                "/music-score/instrument/v1/**",
                                "/music-score/score-collection/v1/**",
                                "/music-score/gaokao-music/v1/**",
                                "/music-score/province-university/v1/**",
                                "/music-score/artist-collection/v1/**",
                                "/music-score/comment/v1/list",
                                "/music-score/comment/v1/children/list/**").permitAll()
                        .anyRequest().authenticated()); // 除上述放行的url,其余全部鉴权认证

//        http.exceptionHandling()
//                .accessDeniedHandler(accessDeniedHandler);
        http.addFilterBefore(authenticationManagerProcessingFilter,
                        UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(jwtAuthenticationTokenFilter,
                        AuthenticationManagerProcessingFilter.class);
        return http.build();
    }
}

认证执行流程实现类:

@Slf4j
@Component
public class AuthenticationManagerProcessingFilter extends AbstractAuthenticationProcessingFilter {
    private final boolean postOnly = false;
    private final ObjectMapper objectMapper = JacksonInstance.getInstance();
    @Resource
    private JwtUtil jwtUtil;
    @Resource
    private RedissonCache redissonCache;
    @Resource
    private AuthStrategyContent authStrategyContent;
    @Resource
    private SecurityThreadPoolConfig securityThreadPoolConfig;

    @Lazy
    @Autowired
    public AuthenticationManagerProcessingFilter(AuthenticationManager authenticationManager,
                                                 AuthenticationSuccessFilter authenticationSuccessHandler,
                                                 AuthenticationFailureFilter authenticationFailureHandler) {
        super("/member/auth/v1/login");
        setAuthenticationManager(authenticationManager);
        setAuthenticationSuccessHandler(authenticationSuccessHandler);
        setAuthenticationFailureHandler(authenticationFailureHandler);
    }

    /**
     * 使用相同的凭证执行身份验证
     *
     * @param request  从中提取参数并执行身份验证
     * @param response 如果实现必须将重定向作为多阶段身份验证过程的一部分(例如OpenID),则可能需要此响应。
     * @return 通过身份验证的用户。返回的对象必须是Authentication的实现
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }
        // 获取用户信息
        InputUserAuth inputUserAuth = getUserByRequest(request);
        if (inputUserAuth == null) {
            throw new AuthenticationServiceException(EnumHttpCode.BAD_REQUEST.getMsg() + "用户信息不能为空!");
        }
        // 验证用户信息
        Authentication authentication = verifyAuthInfo(inputUserAuth);

        UserAuth userAuth = (UserAuth) authentication.getPrincipal();
        if (userAuth == null) {
            throw new AuthenticationServiceException(EnumHttpCode.LOGIN_ERROR.getMsg());
        }
        // 登录成功后将密码置空
        userAuth.setPassword(null);
        // 统计登录次数
        loginCount(request, userAuth);
        //获取userId
        Long id = userAuth.getId();
        OutputUserAuth outputUserAuth = BeanCopyUtils.copyBean(userAuth, OutputUserAuth.class);
        try {
            redissonCache.setCacheObject(ConstantsCache.LOGIN_TOKEN_KEY + id,
                    outputUserAuth,
                    jwtUtil.getExpire(),
                    TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.error("redisson初始化失败", e);
            throw new AuthenticationServiceException(EnumHttpCode.SYSTEM_ERROR.getMsg());
        }
        //通过后用userid生成一个jwt存入ResponseResult
        String jwt = jwtUtil.createJWT(String.valueOf(id));
        userAuth.setTokenHead(JwtUtil.JWT_TOKEN_PREFIX);
        userAuth.setToken(jwt);
        return authentication;
    }

    /**
     * 获取request中的json用户信息
     *
     * @param request 请求
     * @return 用户信息
     * @throws IOException IO异常
     */
    private InputUserAuth getUserByRequest(HttpServletRequest request) throws IOException {
        StringBuilder sb = new StringBuilder();
        InputStream is = request.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);
        String s = "";
        while ((s = br.readLine()) != null) {
            sb.append(s);
        }
        String userInfo = sb.toString();
        InputUserAuth inputUserAuth = null;
        try {
            inputUserAuth = objectMapper.readValue(userInfo, InputUserAuth.class);
        } catch (JsonProcessingException e) {
            log.info("json转换异常: {}", e.getMessage());
            throw new AuthenticationServiceException(EnumHttpCode.BAD_REQUEST.getMsg() + "json转换异常");
        }
        return inputUserAuth;
    }

    /**
     * 验证用户信息
     *
     * @param inputUserAuth 用户信息
     * @return 认证信息
     */
    private Authentication verifyAuthInfo(InputUserAuth inputUserAuth) {
        String authType = inputUserAuth.getAuthType();
        // 认证策略模式
        AuthEnums enumerateInstances = AuthEnums.getEnum(authType);
        if (enumerateInstances == null) {
            throw new AuthenticationServiceException(EnumHttpCode.BAD_REQUEST.getMsg() + "authType认证方式错误!");
        }
        return authStrategyContent.authType(enumerateInstances, inputUserAuth, this.getAuthenticationManager());
    }

    /**
     * 登录次数统计
     */
    private void loginCount(HttpServletRequest request, UserAuth userAuth) {
        Executor asyncExecutor = securityThreadPoolConfig.getAsyncExecutor();
        CompletableFuture.runAsync(() -> {
            try {
                log.info("当前线程id: {},当前线程名称: {}",
                        Thread.currentThread().getId(),
                        Thread.currentThread().getName());
                // 登录次数统计 联网查询ip地址
                String ip = IpUtil.getIp(request);
                String ipAddr = String.valueOf(IpUtil.getIpAddr(ip));
                userAuth.setIpAddr(ipAddr);
                userAuth.setStatus(ConstantsSystemLog.USER_LOGIN_STATUS_VALUE);
                log.info("{}", objectMapper.writeValueAsString(userAuth));
            } catch (JsonProcessingException e) {
                log.error("登录次数统计异常: {}", e.getMessage());
            }
        }, asyncExecutor);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

最难不过坚持丶渊洁

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

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

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

打赏作者

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

抵扣说明:

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

余额充值