【发布时间】:2015-12-30 22:04:13
【问题描述】:
使用 Spring Security 4.02,任何人都可以提供一些提示,说明我在使用多个 PreAuthenticatedAuthenticationProvider 时如何处理 UsernameNotFoundException一个特定的 URL 而不是表单登录页面?
让我进一步解释一下我在访问受代理后 SSO 保护的 Web 应用程序时要完成的工作。并非所有通过 SSO 身份验证的用户都可以访问此应用程序。所以我需要考虑 3 种访问场景:
- 经过身份验证的用户(存在标头)已获得授权(用户名/角色存在于应用的数据库中)
- 经过身份验证的用户(存在标头)未经授权(用户名/角色不存在于应用的数据库中)
- 应用数据库中存在用户名/角色的未经身份验证的用户
访问网站时的操作应该是:
- 经过身份验证/授权的用户直接进入目标 URL
- 经过身份验证/未经授权的用户被重定向到错误/信息页面
- 未经身份验证的用户被重定向到表单登录页面进行身份验证
在我当前的配置下,场景 1 和 3 似乎可以正常工作。对于场景 2,我尝试将 RequestHeaderAuthenticationFilter#setExceptionIfHeaderMissing 设置为 true 和 false。
如果setExceptionIfHeaderMissing=false,经过身份验证/未经授权的请求由ExceptionTranslationFilter 处理,其中AccessDeniedException 被抛出,用户被重定向到表单登录页面。
如果setExceptionIfHeaderMissing=true,经过身份验证/未经授权的请求遇到来自AbstractPreAuthenticatedProcessingFilter.doAuthenticate的PreAuthenticatedCredentialsNotFoundException并返回HTTP 500。
所以我已经阅读并重新阅读了 Spring Security 参考和 api 文档,并搜索了网络,但无法完全弄清楚我需要做什么。我想我需要以某种方式启用某种过滤器或处理程序来捕获带有重定向响应的PreAuthenticatedCredentialsNotFoundException。但我似乎无法用所有可用的弹簧工具来实现它。有人可以提供一些细节吗?非常感谢提前!
这是我的配置:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final String AUTHENTICATION_HEADER_NAME = "PKE_SUBJECT";
@Autowired
CustomUserDetailsServiceImpl customUserDetailsServiceImpl;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(preAuthenticatedAuthenticationProvider());
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
auth.userDetailsService(customUserDetailsServiceImpl);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().and()
.authorizeRequests()
.antMatchers("/javax.faces.resource/**", "/resources/**", "/templates/**", "/public/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/public/welcome.xhtml")
.and()
.addFilter(requestHeaderAuthenticationFilter());
}
@Bean PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider() throws Exception {
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
provider.setPreAuthenticatedUserDetailsService(userDetailsServiceWrapper());
return provider;
}
@Bean
public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter() throws Exception {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
filter.setPrincipalRequestHeader(AUTHENTICATION_HEADER_NAME);
filter.setAuthenticationManager(authenticationManagerBean());
filter.setExceptionIfHeaderMissing(true);
return filter;
}
@Bean
public UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>
userDetailsServiceWrapper() throws Exception {
UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> wrapper
= new UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>();
wrapper.setUserDetailsService(customUserDetailsServiceImpl);
return wrapper;
}
}
我自定义的 UserDetailsService:
@Service("customUserDetailsService")
public class CustomUserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserRepo userRepo;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetailDO userDetail = userRepo.getUserDetailById(username);
if(userDetail == null) {
throw new UsernameNotFoundException("user is not authorized for this application");
}
List<UserRoleDO> roles = userRepo.getRolesByUsername(username);
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
if(CollectionUtils.isNotEmpty(roles)) {
for(UserRoleDO role : roles) {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getRole());
authorities.add(authority);
}
}
UserDetails user = new User(username, "N/A", authorities);
return user;
}
}
【问题讨论】:
标签: spring authentication spring-security authorization pre-authentication