关于#SpringSecurity#的问题,如何解决?

SpringSecurity6.1

自己重写UsernamePasswordAuthenticationFilter

package com.hw.example.admin.config.security.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hw.example.admin.config.security.domain.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.IOException;

/**
 * @author : guanzheng
 * @date : 2023/6/26 15:17
 */
public class JsonLoginFilter extends UsernamePasswordAuthenticationFilter {

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //获取请求头,据此判断请求参数类型
        String contentType = request.getContentType();
        if (MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(contentType) || MediaType.APPLICATION_JSON_UTF8_VALUE.equalsIgnoreCase(contentType)) {
            //说明请求参数是 JSON
            if (!request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
            }
            String username = null;
            String password = null;
            try {
                //解析请求体中的 JSON 参数
                User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
                username = user.getUsername();
                username = (username != null) ? username.trim() : "";
                password = user.getPassword();
                password = (password != null) ? password : "";
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            //构建登录令牌
            UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
                    password);
            // Allow subclasses to set the "details" property
            setDetails(request, authRequest);
            //执行真正的登录操作
            Authentication auth = this.getAuthenticationManager().authenticate(authRequest);

            SecurityContextHolder.getContext().setAuthentication(auth);

            return auth;
        }
        return super.attemptAuthentication(request,response);
    }
}

package com.hw.example.admin.config.security;

import com.hw.example.admin.config.security.filter.JsonLoginFilter;
import com.hw.example.admin.config.security.handler.LoginFailureHandler;
import com.hw.example.admin.config.security.handler.LoginSuccessHandler;
import com.hw.example.admin.config.security.handler.LogoutHandler;
import com.hw.example.admin.config.security.handler.MyPasswordEncoder;
import com.hw.example.admin.config.security.service.UserDetailServiceImpl;
import com.hw.example.common.util.http.HttpUtil;
import com.hw.example.common.web.component.R;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.IOException;

/**
 * @author : guanzheng
 * @date : 2023/6/25 9:03
 */
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Autowired
    UserDetailServiceImpl userDetailService;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        //认证请求配置
        http.authorizeHttpRequests()
                //允许匿名访问的地址,写在最前边
                .requestMatchers("/hello").permitAll()
                //其他请求必须认证才能访问
                .anyRequest().authenticated()
                .and()
                .exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
                    @Override
                    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                        HttpUtil.writeResponse(response, R.ok("未登录"));
                    }
                })
                .and()
                //登录表单配置
                .formLogin()
                //登录表单的用户名name
//                .usernameParameter("username")
                //登录表单的密码name
//                .passwordParameter("password")
                .and()
                .logout()
                //退出登录的url
//                .logoutUrl("/logout")
                .logoutSuccessHandler(new LogoutHandler())
                //清除session
//                .invalidateHttpSession(true)
                //清除认证信息
//                .clearAuthentication(true)
                .and()
                //关闭csrf
                .csrf().disable()
        //添加自定义过滤器
        .addFilterAt(myJsonLoginFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
//    @Bean
//    public InMemoryUserDetailsManager userDetailsService() {
//        UserDetails user = User.withDefaultPasswordEncoder()
//                .username("aaa")
//                .password("111aaa")
//                .roles("USER")
//                .build();
//        return new InMemoryUserDetailsManager(user);
//    }

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


    @Bean
    JsonLoginFilter myJsonLoginFilter() throws Exception {
        JsonLoginFilter myJsonLoginFilter = new JsonLoginFilter();
        myJsonLoginFilter.setAuthenticationSuccessHandler(new LoginSuccessHandler());
        myJsonLoginFilter.setAuthenticationFailureHandler(new LoginFailureHandler());
        myJsonLoginFilter.setAuthenticationManager(authenticationManager());
        return myJsonLoginFilter;
    }



    @Bean
    AuthenticationManager authenticationManager(){
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        //设置用户信息处理器
        daoAuthenticationProvider.setUserDetailsService(userDetailService);
        //设置密码处理器
        daoAuthenticationProvider.setPasswordEncoder(new MyPasswordEncoder());
        ProviderManager pm = new ProviderManager(daoAuthenticationProvider);
        return pm;
    }

}




git地址:https://gitee.com/guan0207/puyu

但是登录成功后,还是未登录状态,SecurityContextHolder.getContext().getAuthentication().getPrincipal()取出来是anonymousUser是咋回事?

可能的原因是在SecurityConfiguration类中配置的身份验证提供程序(DaoAuthenticationProvider)未正确地加载用户详细信息或密码处理器。
确保以下内容:
1.UserDetailServiceImpl类实现了UserDetailsService接口,并正确加载用户详细信息。
2.MyPasswordEncoder类实现了PasswordEncoder接口,并正确处理密码。
3.在authenticationManager()方法中,将用户信息处理器(userDetailService)和密码处理器(MyPasswordEncoder)正确配置给DaoAuthenticationProvider。

调试和检查上述配置,确保用户详细信息和密码被正确加载和处理。如果问题仍然存在,那问题就出在UserDetailServiceImpl或MyPasswordEncoder的代码里了

引用chatgpt内容作答:
根据您提供的代码,问题可能出在您的配置文件SecurityConfiguration中。在filterChain方法中,您使用了自定义的JsonLoginFilter,但是在创建SecurityFilterChain时没有将该过滤器添加到过滤器链中。

在您的代码中,可以尝试将以下代码添加到filterChain方法中,以将自定义的JsonLoginFilter添加到过滤器链中:

.addFilterBefore(myJsonLoginFilter(), UsernamePasswordAuthenticationFilter.class)

这将在UsernamePasswordAuthenticationFilter之前添加您的自定义过滤器。

修改后的filterChain方法代码如下:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    // 认证请求配置
    http.authorizeHttpRequests()
        // 允许匿名访问的地址,写在最前面
        .requestMatchers("/hello").permitAll()
        // 其他请求必须认证才能访问
        .anyRequest().authenticated()
        .and()
        .exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
            @Override
            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                HttpUtil.writeResponse(response, R.ok("未登录"));
            }
        })
        .and()
        // 登录表单配置
        .formLogin()
        .and()
        .logout()
        .logoutSuccessHandler(new LogoutHandler())
        .and()
        // 关闭 CSRF
        .csrf().disable()
        // 添加自定义过滤器
        .addFilterBefore(myJsonLoginFilter(), UsernamePasswordAuthenticationFilter.class);

    return http.build();
}

这样应该能够正确地将自定义过滤器添加到过滤器链中,使登录成功后的认证信息得以保存。

基于new bing部分指引作答:
出现"anonymousUser"作为用户身份的情况通常是因为在登录成功后,Spring Security并没有正确地将认证信息与当前的安全上下文关联起来。

可能的原因和解决方法如下:

  1. 检查登录配置:确保正确配置了登录验证过程,并在认证成功后使用适当的机制将认证信息和安全上下文关联起来。可以检查登录验证过程中是否正确设置了UsernamePasswordAuthenticationToken以及调用了AuthenticationManager进行认证。

  2. 检查登录成功处理器:在登录成功后,应该使用合适的成功处理器来处理认证成功事件。这个处理器应该将认证信息设置到安全上下文中。可以确认您是否实现了AuthenticationSuccessHandler接口,并在其中正确地处理认证成功后的操作。

  3. 检查会话管理配置:如果基于会话的认证管理被使用,需要确保会话管理配置正确。例如,需要适当地配置SessionManagementConfigurer以启用会话管理,并在登录时创建会话。

  4. 检查用户身份提供者:如果您使用了自定义的用户身份提供者,需要确保提供者正确返回有效的认证信息。可以检查自定义的UserDetailsServiceAuthenticationProvider实现,确认是否在认证成功时返回了正确的认证对象。

以上是一些可能导致"anonymousUser"身份的常见问题和解决方法。然而,具体解决方法可能因您的代码和配置而异。如果问题仍然存在,并且您无法确定出错的原因,建议您仔细检查您的代码和相关配置,以及查阅Spring Security的官方文档或寻求开发社区的支持来解决问题。

你自定义的 JsonLoginFilter 中还没有设置成功后的跳转地址,重写 onSuccessfulAuthentication() 方法,调用父类的 redirectStrategy 来跳转到成功后的 URL 地址

重写方法别一次性写完,一点点加,这样改错相对容易点
SpringSecurity存在的问题及解决方案

https://blog.csdn.net/Stubborn_bull/article/details/126856420

登录成功的时候,少了一部把上下文存入session

public class LoginSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
        HttpUtil.writeResponse(response,R.ok("登录成功").put("authentication",authentication));
    }
}