我的身份验证过滤器未根据请求启动。
我有2种安全配置,一种仅用于登录端点,通过AuthenticationFromCredentialsFilter
过滤器进行身份验证,另一种用于其他端点,通过AuthenticationFromTokenFilter
过滤器进行身份验证。
我希望调用过滤器的attemptAuthentication
方法,但不会。
是否更愿意在过滤器中而不是在登录控制器中对凭据进行身份验证并创建令牌?
目前存在登录控制器,但是它不应该存在,因为它的工作应该由过滤器完成。
我将它们分别设置为安全配置:
@EnvProd
@EnableWebSecurity
@ComponentScan(nameGenerator = PackageBeanNameGenerator.class, basePackages = { "com.thalasoft.user.rest.security", "com.thalasoft.user.rest.filter" })
public class WebSecurityConfiguration {
@Order(1)
@Configuration
public class CredentialsConfiguration extends WebSecurityConfigurerAdapter {
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
public AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter() throws Exception {
AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter = new AuthenticationFromCredentialsFilter();
authenticationFromCredentialsFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationFromCredentialsFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/users/login")
.addFilterBefore(authenticationFromCredentialsFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/api/users/login").permitAll()
.anyRequest().authenticated();
}
}
@Order(2)
@Configuration
public class TokenConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationFromTokenFilter authenticationFromTokenFilter;
@Autowired
private RESTAuthenticationEntryPoint restAuthenticationEntryPoint;
@Autowired
private SimpleCORSFilter simpleCORSFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable();
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.headers()
.cacheControl().disable()
.frameOptions().disable();
http
.httpBasic()
.authenticationEntryPoint(restAuthenticationEntryPoint);
http
.addFilterBefore(simpleCORSFilter, UsernamePasswordAuthenticationFilter.class);
http
.addFilterBefore(authenticationFromTokenFilter, UsernamePasswordAuthenticationFilter.class);
http.antMatcher("/api/**")
.addFilterBefore(authenticationFromTokenFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/error").permitAll()
.antMatchers("/admin/**").hasRole(UserDomainConstants.ROLE_ADMIN)
.anyRequest().authenticated();
}
}
}
这是两个过滤器:
public class AuthenticationFromCredentialsFilter extends UsernamePasswordAuthenticationFilter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private TokenAuthenticationService tokenAuthenticationService;
@Autowired
CredentialsService credentialsService;
@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
throws AuthenticationException {
try {
CredentialsResource credentialsResource = new ObjectMapper().readValue(req.getInputStream(),
CredentialsResource.class);
return authenticationManager.authenticate(credentialsService.authenticate(credentialsResource));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authentication) throws IOException, ServletException {
tokenAuthenticationService.addTokenToResponseHeader(response, authentication);
}
}
public class AuthenticationFromTokenFilter extends UsernamePasswordAuthenticationFilter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private TokenAuthenticationService tokenAuthenticationService;
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
tokenAuthenticationService.authenticate(request);
return authenticationManager.authenticate(tokenAuthenticationService.authenticate(request));
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authentication) throws IOException, ServletException {
}
}
这是一个登录请求的示例,该登录请求应由安全配置中的AuthenticationFromCredentialsFilter
过滤器捕获,但不是,因此允许其进入控制器并给出一个{{1 }}状态:
201
我是否可以期望登录请求启动$ curl -i -H "Accept:application/json" -H "Content-Type: application/json" "http://localhost:8080/api/users/login" -X POST -d "{ \"email\" : \"xxxxxx@yahoo.se\", \"password\" : \"xxxxx\" }"
HTTP/1.1 201
Cache-Control: no-store
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzQwNTE5MjYsInN1YiI6Im1pdHRpcHJvdmVuY2VAeWFob28uc2UifQ.LOJvr5jWouWsLN_Pinlr_F5dntON45hwpUFVmXD2Xqo
Location: http://localhost:8080/api/users/1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 05 Aug 2018 05:32:07 GMT
{"firstname":"Stephane","lastname":"Eybert","email":"xxxxx@yahoo.se","confirmedEmail":false,"password":"bWl0dGlwcm92ZW5jZUB5YWhvby5zZTptaWduZXQxYjE4ZDQ5MS00ZGRhLTQxZWYtYWM5ZS04N2Y5ODk=","workPhone":null,"userRoles":[{"role":"ROLE_ADMIN","id":1}],"_links":{"self":{"href":"http://localhost:8080/api/users/1"},"roles":{"href":"http://localhost:8080/api/users/1/roles"}},"id":1}[stephane@stephane-ThinkPad-X201 user-rest (master)]
过滤器?使用过滤器进行身份验证并使用令牌响应吗?而且根本没有调用登录控制器吗?
这是密码更改请求的另一个示例,应该由安全配置中的AuthenticationFromCredentialsFilter
过滤器捕获,但是没有,因此允许其继续向控制器发送并以{{ 1}}状态:
AuthenticationFromTokenFilter
对于登录请求使用200
而不是$ curl -i -H "Accept:application/json" -H "Content-Type: application/json" "http://localhost:8080/api/users/1/password" -X PUT -d "\"xxxxx\""
HTTP/1.1 200
Cache-Control: no-store
Location: http://localhost:8080/api/users/1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 04 Aug 2018 20:23:17 GMT
{"firstname":"Stephane","lastname":"Eybert","email":"xxxx@yahoo.se","confirmedEmail":false,"password":"bWl0dGlwcm92ZW5jZUB5YWhvby5zZTptaWduZXRhYTA4OTNiZS0yMzZlLTQ3ZjktOTE2Ny0zOTU0NTY=","workPhone":null,"userRoles":[{"role":"ROLE_ADMIN","id":1}],"_links":{"self":{"href":"http://localhost:8080/api/users/1"},"roles":{"href":"http://localhost:8080/api/users/1/roles"}},"id":1}
过滤器怎么办?在CustomAuthenticationProvider implements AuthenticationProvider
下仍然可行吗?
我在想类似的东西:
AuthenticationFromCredentialsFilter
身份验证提供者为:
Spring Boot 2.0.3
更新:我也尝试了这种配置,但是它并没有改变问题:
@Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
答案 0 :(得分:0)
您的过滤器扩展了var fe = [];
var obj = [];
homeShapeShift.forEach(function(element) {
element.forEach(function(element) {
if(element.text !== 'ZEC'){
obj.push(element);
}
});
fe.push(obj);
obj = [];
});
,默认情况下,此过滤器仅适用于URL UsernamePasswordAuthenticationFilter
,请参见UsernamePasswordAuthenticationFilter
:
此过滤器默认情况下会响应URL
/login
。
如果要将默认设置更改为另一个URL,请参见AbstractAuthenticationProcessingFilter#setFilterProcessesUrl
:
设置用于确定是否需要身份验证的URL
您修改的代码:
/login
如果要使用模式,请参见AbstractAuthenticationProcessingFilter:
如果请求与
public AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter() throws Exception { AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter = new AuthenticationFromCredentialsFilter(); authenticationFromCredentialsFilter.setAuthenticationManager(authenticationManagerBean()); authenticationFromCredentialsFilter.setFilterProcessesUrl("/api/users/login"); return authenticationFromCredentialsFilter; }
相匹配,此过滤器将拦截请求并尝试从该请求执行身份验证。
您修改的代码:
setRequiresAuthenticationRequestMatcher(RequestMatcher)
答案 1 :(得分:0)
我最终可以在Spring Boot 2
环境下启动过滤器,并针对各自的请求启动过滤器。
凭据过滤器会根据对/users/login
端点的登录请求触发。
令牌过滤器会在登录请求以外的任何请求上触发。
要将URL模式分配给过滤器,过滤器必须扩展AbstractAuthenticationProcessingFilter
类。这两个过滤器扩展了该类。模式是在其构造函数中指定的。
凭据过滤器为:
public class AuthenticationFromCredentialsFilter extends AbstractAuthenticationProcessingFilter {
@Autowired
private TokenAuthenticationService tokenAuthenticationService;
@Autowired
CredentialsService credentialsService;
public AuthenticationFromCredentialsFilter(final RequestMatcher requestMatcher) {
super(requestMatcher);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
throws AuthenticationException {
try {
CredentialsResource credentialsResource = new ObjectMapper().readValue(req.getInputStream(),
CredentialsResource.class);
return credentialsService.authenticate(credentialsResource);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authentication) throws IOException, ServletException {
tokenAuthenticationService.addTokenToResponseHeader(response, authentication);
}
}
请注意,在成功进行身份验证后,不会继续使用过滤器链,即没有对filterChain.doFilter(httpRequest, httpResponse);
的调用,因为不需要命中控制器端点,因为响应已经与令牌一起发送回了
使用@Bean
注释显式实例化它以指定匹配器模式:
@Bean
public AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter() throws Exception {
AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter = new AuthenticationFromCredentialsFilter(new AntPathRequestMatcher("/users/login", RequestMethod.POST.name()));
authenticationFromCredentialsFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationFromCredentialsFilter;
}
令牌过滤器为:
public class AuthenticationFromTokenFilter extends AbstractAuthenticationProcessingFilter {
@Autowired
private TokenAuthenticationService tokenAuthenticationService;
public AuthenticationFromTokenFilter(final RequestMatcher requestMatcher) {
super(requestMatcher);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
return tokenAuthenticationService.authenticate(request);
}
@Override
protected void successfulAuthentication(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
FilterChain filterChain, Authentication authResult) throws IOException, ServletException {
filterChain.doFilter(httpRequest, httpResponse);
}
}
使用@Bean
注释显式实例化它以指定匹配器模式:
@Bean
public AuthenticationFromTokenFilter authenticationFromTokenFilter() throws Exception {
AuthenticationFromTokenFilter authenticationFromTokenFilter = new AuthenticationFromTokenFilter(new NegatedRequestMatcher(new AntPathRequestMatcher("/users/login", RequestMethod.POST.name())));
authenticationFromTokenFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationFromTokenFilter;
}
与先前的凭据过滤器相反,此过滤器需要在配置中明确指定:
http
.addFilterBefore(authenticationFromTokenFilter, UsernamePasswordAuthenticationFilter.class)
为避免此过滤器在过滤器链中启动两次,bean指示Spring Boot不要自动注入它:
@Bean
FilterRegistrationBean<AuthenticationFromTokenFilter> disableAutoRegistration(final AuthenticationFromTokenFilter filter) {
final FilterRegistrationBean<AuthenticationFromTokenFilter> registration = new FilterRegistrationBean<AuthenticationFromTokenFilter>(filter);
registration.setEnabled(false);
return registration;
}
请注意,在成功通过身份验证后,将继续执行过滤器链,即调用filterChain.doFilter(httpRequest, httpResponse);
,因为在成功通过身份验证后需要命中控制器端点。
完整配置为:
@ComponentScan(nameGenerator = PackageBeanNameGenerator.class, basePackages = { "com.thalasoft.user.rest.security",
"com.thalasoft.user.rest.service", "com.thalasoft.user.rest.filter" })
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private RESTAuthenticationEntryPoint restAuthenticationEntryPoint;
@Autowired
private SimpleCORSFilter simpleCORSFilter;
@Autowired
AuthenticationFromTokenFilter authenticationFromTokenFilter;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter() throws Exception {
AuthenticationFromCredentialsFilter authenticationFromCredentialsFilter = new AuthenticationFromCredentialsFilter(new AntPathRequestMatcher("/users/login", RequestMethod.POST.name()));
authenticationFromCredentialsFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationFromCredentialsFilter;
}
@Bean
public AuthenticationFromTokenFilter authenticationFromTokenFilter() throws Exception {
AuthenticationFromTokenFilter authenticationFromTokenFilter = new AuthenticationFromTokenFilter(new NegatedRequestMatcher(new AntPathRequestMatcher("/users/login", RequestMethod.POST.name())));
authenticationFromTokenFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationFromTokenFilter;
}
@Bean
FilterRegistrationBean<AuthenticationFromTokenFilter> disableAutoRegistration(final AuthenticationFromTokenFilter filter) {
final FilterRegistrationBean<AuthenticationFromTokenFilter> registration = new FilterRegistrationBean<AuthenticationFromTokenFilter>(filter);
registration.setEnabled(false);
return registration;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();
http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
http.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(simpleCORSFilter, ChannelProcessingFilter.class);
http
.addFilterBefore(authenticationFromTokenFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/", "/error").permitAll()
.antMatchers("/users/login").permitAll()
.antMatchers("/admin/**").hasRole(UserDomainConstants.ROLE_ADMIN)
.anyRequest().authenticated();
}
}
另外,请注意,在此Spring Boot配置中根本没有使用:
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
CredentialsService credentialsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
return credentialsService.authenticate(authentication);
}
@Override
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}
我想知道它适合什么地方以及如何替换凭据过滤器...但这是另一回事了。