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并没有正确地将认证信息与当前的安全上下文关联起来。
可能的原因和解决方法如下:
检查登录配置:确保正确配置了登录验证过程,并在认证成功后使用适当的机制将认证信息和安全上下文关联起来。可以检查登录验证过程中是否正确设置了UsernamePasswordAuthenticationToken
以及调用了AuthenticationManager
进行认证。
检查登录成功处理器:在登录成功后,应该使用合适的成功处理器来处理认证成功事件。这个处理器应该将认证信息设置到安全上下文中。可以确认您是否实现了AuthenticationSuccessHandler
接口,并在其中正确地处理认证成功后的操作。
检查会话管理配置:如果基于会话的认证管理被使用,需要确保会话管理配置正确。例如,需要适当地配置SessionManagementConfigurer
以启用会话管理,并在登录时创建会话。
检查用户身份提供者:如果您使用了自定义的用户身份提供者,需要确保提供者正确返回有效的认证信息。可以检查自定义的UserDetailsService
或AuthenticationProvider
实现,确认是否在认证成功时返回了正确的认证对象。
以上是一些可能导致"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));
}
}