SpringBoot UsernamePasswordAuthenticationFilter问题
SpringBoot UsernamePasswordAuthenticationFilter issue
我正在扩展 UsernamePasswordAuthenticationFilter
以便我可以添加自定义字段以将它们保存到会话中。
public class AuthFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
//String dbValue = request.getParameter("dbParam");
//request.getSession().setAttribute("dbValue", dbValue);
System.out.println("attempting to authentificate");
while (request.getAttributeNames().hasMoreElements()) {
String e = (String) request.getAttributeNames().nextElement();
System.out.println("param name : " + e + " and param value : " + request.getAttribute(e));
}
return super.attemptAuthentication(request, response);
}
}
还有我的 WebSecurityConfig
@Configuration
@EnableWebMvcSecurity
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public AuthFilter customUsernamePasswordAuthenticationFilter()
throws Exception {
AuthFilter customUsernamePasswordAuthenticationFilter = new AuthFilter();
customUsernamePasswordAuthenticationFilter
.setAuthenticationManager(authenticationManagerBean());
return customUsernamePasswordAuthenticationFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(customUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
http.exceptionHandling().accessDeniedPage("/403").and()
.authorizeRequests().antMatchers("/login", "/public/**").permitAll()
.antMatchers("/users/**").hasAuthority("ADMIN")
.anyRequest()
.authenticated().and().formLogin().loginPage("/login")
.defaultSuccessUrl("/index").permitAll().and().logout()
.permitAll();
http.sessionManagement().maximumSessions(1)
.expiredUrl("/login?expired").and()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.eraseCredentials(false)
.userDetailsService(userDetailsService);
}
映射过滤器:'customUsernamePasswordAuthenticationFilter' 到:[/*]
所以我确定过滤器已正确添加,但我永远无法打印出里面的内容,因此在验证期间不会调用它。
我使用 Thymeleaf,没有 xml 配置。
作为@M。 Deinum 建议,
我将 UsernamePasswordAuthenticationFilter
更改为 AbstractAuthenticationProcessingFilter
,名为 super(new AntPathRequestMatcher("/login","POST"));
将 addFilterAfter
更改为 addFilterBefore
和一些代码,它起作用了!
假设您正在使用最新的 Spring Boot (1.2.3),您正在使用 Spring Security 3.2.7 此版本将 UsernamePasswordAuthenticationFilter
映射到 /j_spring_security_check
。但是,当使用基于 java 的配置时,这将更改为 /login
。
你的仍然映射到旧的 URL。要修复此扩展 AbstractAuthenticationProcessingFilter
添加一个默认的无参数构造函数,它调用采用 RequestMatcher
的超级构造函数。这样做的缺点是,如果您仍然需要(或想要扩展)UsernamePasswordAuthenticationFilter
的功能,您将不得不复制它。
public AuthFilter() {
super(new AntPathRequestMatcher("/login","POST"));
}
另一个解决方案是仍然扩展 UsernamePasswordAuthenticationFilter
并从那里调用 setRequiresAuthenticationRequestMatcher
。
public AuthFilter() {
super();
setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
}
或者您从工厂方法中调用该方法。
@Bean
public AuthFilter customUsernamePasswordAuthenticationFilter()
throws Exception {
AuthFilter customUsernamePasswordAuthenticationFilter = new AuthFilter();
customUsernamePasswordAuthenticationFilter
.setAuthenticationManager(authenticationManagerBean());
customUsernamePasswordAuthenticationFilter
.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
return customUsernamePasswordAuthenticationFilter;
}
您的配置还有另一个问题,您的过滤器将永远不会执行,因为它是在默认 UsernamePasswordAuthenticationFilter
之后执行的,并且身份验证已经发生,您的过滤器将永远不会执行。确保它在默认过滤器之前执行。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(customUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
...
}
为了使您的自定义 UsernamePasswordAuthenticationFilter 实施工作,请将 .loginProcessingUrl("/dologin") 添加到您的 HttpSecurity
WebSecurityConfig,这里"/dologin"是html表单元素的action属性值:
@Override
//@Order(Ordered.HIGHEST_PRECEDENCE)
public void configure(HttpSecurity http) throws Exception { // @formatter:off
http
...
...
.formLogin().loginPage("/login")
--> .loginProcessingUrl("/dologin") <-- add here
...
--> .addFilterBefore(new AuthFilter(authenticationManagerBean()),UsernamePasswordAuthenticationFilter.class)
}
接下来是提供自定义UsernamePasswordAuthenticationFilter实现:
public class AuthFilter extends UsernamePasswordAuthenticationFilter {
AuthenticationManager authenticationManager;
private boolean continueChainBeforeSuccessfulAuthentication = false;
public AuthFilter( AuthenticationManager authenticationManager){
this.authenticationManager = authenticationManager;
//idk why I have to do this, otherwise it's null
super.setAuthenticationManager(authenticationManager);
}
public AuthFilter() {}
private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
//path to which this filter will intercept
RequestMatcher customFilterUrl = new AntPathRequestMatcher("/dologin"); <--
//dofilter method is copied from AbstractAuthenticationProcessingFilter
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
//if no match then go to next filter
if (!customFilterUrl.matches(request)) {
chain.doFilter(request, response);
} else {
Authentication authResult;
try {
authResult = this.attemptAuthentication(request, response);
if (authResult == null) {
return;
}
this.sessionStrategy.onAuthentication(authResult, request, response);
} catch (InternalAuthenticationServiceException var8) {
this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
this.unsuccessfulAuthentication(request, response, var8);
return;
} catch (AuthenticationException var9) {
this.unsuccessfulAuthentication(request, response, var9);
return;
}
if (this.continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, chain, authResult);
}
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response)
throws AuthenticationException {
System.out.println("Your prints"); <--
return super.attemptAuthentication(request,response);
}
}
我正在扩展 UsernamePasswordAuthenticationFilter
以便我可以添加自定义字段以将它们保存到会话中。
public class AuthFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
//String dbValue = request.getParameter("dbParam");
//request.getSession().setAttribute("dbValue", dbValue);
System.out.println("attempting to authentificate");
while (request.getAttributeNames().hasMoreElements()) {
String e = (String) request.getAttributeNames().nextElement();
System.out.println("param name : " + e + " and param value : " + request.getAttribute(e));
}
return super.attemptAuthentication(request, response);
}
}
还有我的 WebSecurityConfig
@Configuration
@EnableWebMvcSecurity
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public AuthFilter customUsernamePasswordAuthenticationFilter()
throws Exception {
AuthFilter customUsernamePasswordAuthenticationFilter = new AuthFilter();
customUsernamePasswordAuthenticationFilter
.setAuthenticationManager(authenticationManagerBean());
return customUsernamePasswordAuthenticationFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(customUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
http.exceptionHandling().accessDeniedPage("/403").and()
.authorizeRequests().antMatchers("/login", "/public/**").permitAll()
.antMatchers("/users/**").hasAuthority("ADMIN")
.anyRequest()
.authenticated().and().formLogin().loginPage("/login")
.defaultSuccessUrl("/index").permitAll().and().logout()
.permitAll();
http.sessionManagement().maximumSessions(1)
.expiredUrl("/login?expired").and()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.eraseCredentials(false)
.userDetailsService(userDetailsService);
}
映射过滤器:'customUsernamePasswordAuthenticationFilter' 到:[/*]
所以我确定过滤器已正确添加,但我永远无法打印出里面的内容,因此在验证期间不会调用它。
我使用 Thymeleaf,没有 xml 配置。
作为@M。 Deinum 建议,
我将 UsernamePasswordAuthenticationFilter
更改为 AbstractAuthenticationProcessingFilter
,名为 super(new AntPathRequestMatcher("/login","POST"));
将 addFilterAfter
更改为 addFilterBefore
和一些代码,它起作用了!
假设您正在使用最新的 Spring Boot (1.2.3),您正在使用 Spring Security 3.2.7 此版本将 UsernamePasswordAuthenticationFilter
映射到 /j_spring_security_check
。但是,当使用基于 java 的配置时,这将更改为 /login
。
你的仍然映射到旧的 URL。要修复此扩展 AbstractAuthenticationProcessingFilter
添加一个默认的无参数构造函数,它调用采用 RequestMatcher
的超级构造函数。这样做的缺点是,如果您仍然需要(或想要扩展)UsernamePasswordAuthenticationFilter
的功能,您将不得不复制它。
public AuthFilter() {
super(new AntPathRequestMatcher("/login","POST"));
}
另一个解决方案是仍然扩展 UsernamePasswordAuthenticationFilter
并从那里调用 setRequiresAuthenticationRequestMatcher
。
public AuthFilter() {
super();
setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
}
或者您从工厂方法中调用该方法。
@Bean
public AuthFilter customUsernamePasswordAuthenticationFilter()
throws Exception {
AuthFilter customUsernamePasswordAuthenticationFilter = new AuthFilter();
customUsernamePasswordAuthenticationFilter
.setAuthenticationManager(authenticationManagerBean());
customUsernamePasswordAuthenticationFilter
.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
return customUsernamePasswordAuthenticationFilter;
}
您的配置还有另一个问题,您的过滤器将永远不会执行,因为它是在默认 UsernamePasswordAuthenticationFilter
之后执行的,并且身份验证已经发生,您的过滤器将永远不会执行。确保它在默认过滤器之前执行。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(customUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
...
}
为了使您的自定义 UsernamePasswordAuthenticationFilter 实施工作,请将 .loginProcessingUrl("/dologin") 添加到您的 HttpSecurity WebSecurityConfig,这里"/dologin"是html表单元素的action属性值:
@Override
//@Order(Ordered.HIGHEST_PRECEDENCE)
public void configure(HttpSecurity http) throws Exception { // @formatter:off
http
...
...
.formLogin().loginPage("/login")
--> .loginProcessingUrl("/dologin") <-- add here
...
--> .addFilterBefore(new AuthFilter(authenticationManagerBean()),UsernamePasswordAuthenticationFilter.class)
}
接下来是提供自定义UsernamePasswordAuthenticationFilter实现:
public class AuthFilter extends UsernamePasswordAuthenticationFilter {
AuthenticationManager authenticationManager;
private boolean continueChainBeforeSuccessfulAuthentication = false;
public AuthFilter( AuthenticationManager authenticationManager){
this.authenticationManager = authenticationManager;
//idk why I have to do this, otherwise it's null
super.setAuthenticationManager(authenticationManager);
}
public AuthFilter() {}
private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
//path to which this filter will intercept
RequestMatcher customFilterUrl = new AntPathRequestMatcher("/dologin"); <--
//dofilter method is copied from AbstractAuthenticationProcessingFilter
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
//if no match then go to next filter
if (!customFilterUrl.matches(request)) {
chain.doFilter(request, response);
} else {
Authentication authResult;
try {
authResult = this.attemptAuthentication(request, response);
if (authResult == null) {
return;
}
this.sessionStrategy.onAuthentication(authResult, request, response);
} catch (InternalAuthenticationServiceException var8) {
this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
this.unsuccessfulAuthentication(request, response, var8);
return;
} catch (AuthenticationException var9) {
this.unsuccessfulAuthentication(request, response, var9);
return;
}
if (this.continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, chain, authResult);
}
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response)
throws AuthenticationException {
System.out.println("Your prints"); <--
return super.attemptAuthentication(request,response);
}
}