Spring Boot 添加CustomFilter时,向控制器中的AuthenticationPrincipal传递了空值

cedebl8k  于 2023-03-18  发布在  Spring
关注(0)|答案(1)|浏览(138)

我添加了“TenantID”到登录页面,使用用户名和密码验证登录用户,并添加了“CustomAuthenticationFilter”,“CustomAuthenticationProvider”,“MethodSecurityConfig”,“SuccessHandler”并将其附加到“SecurityConfig”。
然后,它可以通过”成功处理器“上的“安全上下文保持器.getContext(). getAuthentication().getPrincipal()”获取用户数据,该“成功处理器”实现了“验证成功处理器”。
但是,控制器上的AuthenticationPrincipal为空。我想告诉我原因以及如何修复它。
用途:SpringBoot3.0.4、SpringSecurity6.0.2使用“网络安全配置器适配器”的“SpringSecurity5.2.2”没有问题
安全配置

@EnableWebSecurity
@Configuration
public class SecurityConfig  {

    @Autowired
    @Qualifier("SuccessHandler")
    AuthenticationSuccessHandler successHandler;

    @Autowired
    @Qualifier("UserDetailsServiceImpl")
    private UserDetailsService userDetailsService;

    //Bean by PasswordEncoder
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private DataSource dataSource;

    @Autowired
    @Qualifier("CustomAuthenticationProvider")
    private AuthenticationProvider authenticationProvider;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.disable());
        http.headers(head -> head.frameOptions().disable());
        http
            .authorizeHttpRequests(authz -> authz.requestMatchers("/login").permitAll() 
                .anyRequest().authenticated()); 
        http
            .formLogin()
//                .loginProcessingUrl("/login") 
                .loginPage("/login"); 
//                .failureUrl("/login?error") 
//                .usernameParameter("userId") 
//                .passwordParameter("password") 
//                //.defaultSuccessUrl("/home", true); 
//                .successHandler(successHandler);

        // Login-filters
        CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
        filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login", "POST"));
        filter.setAuthenticationManager(authenticationManagerBean());
        filter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/login?error"));
        filter.setAuthenticationSuccessHandler(successHandler);
        http.addFilterBefore(filter, CustomAuthenticationFilter.class);

        // Logout
        http
            .logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login");

        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        AuthenticationManager auth = new ProviderManager(authenticationProvider);
        System.out.println("AuthenticationManager");
        System.out.println(auth);
        
        return new ProviderManager(authenticationProvider);
    }
}

自定义身份验证筛选器

public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

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

        // ユーザー生成
        AppUserDetails user = new AppUserDetails();
        user.setTenantId(request.getParameter("tenantId"));
        user.setUserId(request.getParameter("userId"));

        // パスワード取得
        String password = obtainPassword(request);

        // トークンの作成
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(user, password);

        // リクエストの詳細をセットしておく
        setDetails(request, authRequest);
        
        Authentication authz = this.getAuthenticationManager().authenticate(authRequest);
        System.out.println("aaaaaaaauth");
        System.out.println(authz);
        // 認証の実施
        return authz;
    }
}

自定义身份验证提供程序

@Component("CustomAuthenticationProvider")
@Slf4j
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    UserDetailsServiceImpl service;

    @Autowired
    MessageSource messageSource;

    /** メッセージのキー(認証失敗) */
    private static final String BAD_CREDENTIALS = "AbstractUserDetailsAuthenticationProvider.badCredentials";

    /*
     * 認証処理を行うメソッド.
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        // ユーザー取得(リクエストから)
        AppUserDetails user = (AppUserDetails)authentication.getPrincipal();

        // パスワード取得(リクエストから)
        String password = (String)authentication.getCredentials();

        // ユーザー取得(DBから)
        user = (AppUserDetails) service.loadUserByUsernameAndTenantId(user.getUserId(), user.getTenantId());

        // パスワードチェック
        checkPassword(password, user.getPassword());

        // 各種チェック
        UserDetailsChecker checker = new AccountStatusUserDetailsChecker();
        checker.check(user);

        // トークンを生成
        return new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
    }

    /*
     * 認証処理を実施するクラスの制限.
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }

    /**
     * パスワードチェック.
     */
    private void checkPassword(String rawPassword, String encodedPassword) {

        PasswordEncoder encoder = new BCryptPasswordEncoder();

        // パスワードが一致しているかどうか
        if(encoder.matches(rawPassword, encodedPassword) == false) {
            // エラーメッセージ取得
            String message = messageSource.getMessage(BAD_CREDENTIALS,
                    null,
                    Locale.getDefault());
            // 例外を投げる
            throw new BadCredentialsException(message);
        }
    }
}

方法安全配置

@Configuration
@EnableMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true)
public class MethodSecurityConfig {
    
    
}

成功处理程序

@Component("SuccessHandler")
@Slf4j
public class SuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    UserDetailsServiceImpl service;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
            HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {

        log.info("ログイン成功イベント開始");

        // ユーザー情報の取得
        AppUserDetails user = (AppUserDetails) SecurityContextHolder
                .getContext()
                .getAuthentication()
                .getPrincipal();
        log.info(user.toString());

        String redirectPath = request.getContextPath();

        // パスワード更新日付のチェック
        if(user.getPassUpdateDate().after(new Date())) {
            // パスワード期限が切れてない
            log.info("遷移先:ホーム");
            redirectPath += "/home";

        } else {
            // パスワード期限切れ
            log.info("遷移先:パスワード変更");
            redirectPath += "/password/change";

        }

        log.info("ログイン成功イベント終了");
        response.sendRedirect(redirectPath);
    }
}

家庭控制器

public class HomeController {
@GetMapping("/home")
@PreAuthorize("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_GENERAL')") 
public String getHome(Model model,
        @AuthenticationPrincipal AppUserDetails user) {

    log.info("HomeController Start");

    
    log.info(user.toString());

    log.info("HomeController End");

    return "home";
}

我希望将用户数据传递到控制器中的AuthenticationPrincipal

oyxsuwqo

oyxsuwqo1#

我下定决心
我将这段代码添加到“响应”之前的“成功处理程序”中。“

SecurityContextRepository securityContextRepository =
            new HttpSessionSecurityContextRepository();
    SecurityContext context = securityContextHolderStrategy.createEmptyContext();
    context.setAuthentication(authentication); 
    securityContextHolderStrategy.setContext(SecurityContextHolder.getContext());
    securityContextRepository.saveContext(context, request, response);

并添加以下代码

private final SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();

此问题的原因是未存储“身份验证”
参考:https://docs.spring.io/spring-security/reference/servlet/authentication/session-management.html#store-authentication-manually

相关问题