服务在网关在受信任空间中运行之后(gateWay验证OAuth令牌,并仅向服务提供唯一的用户ID,否则它将重定向到身份验证服务)。
我想在服务中使用Spring Security来验证userId的权限。
所以我加了CustomUserDetailsService
@Service(“ userDetailsService”)
公共类CustomUserDetailsService实现UserDetailsService {
@Autowired(必填=假)
私有ContextSsoActiveProfileIdProvider contextSsoActiveProfileIdProvider;
@Autowired
私有GrantedAuthorityService grantAuthorityService;
@Override
公共用户loadUserByUsername(最终字符串用户名)抛出UsernameNotFoundException {
//使用身份验证服务验证它,但没有令牌,仅用户ID,因此信任网关服务。
返回新用户(
String.valueOf(contextSsoActiveProfileIdProvider.getSsoActiveProfileId()),
“ authenticatedWithGateWay”,
allowedAuthorityService.getGrantedAuthoritiesForCurrentUser()
);
}
}
其中contextSsoActiveProfileIdProvider.getSsoActiveProfileId()返回uniqueUserId,grantAuthorityService.getGrantedAuthoritiesForCurrentUser()返回权限。
该服务在受信任区域中启动,因此我已通过以下方式配置安全性:
@EnableWebSecurity
@组态
公共类SecurityConfiguration扩展了WebSecurityConfigurerAdapter {
@Autowired
私人UserDetailsService userDetailsService;
@Override
受保护的void configure(HttpSecurity http)抛出异常{
http
.authorizeRequests()
.antMatchers(“ / **”)。permitAll();
}
@Override
受保护的UserDetailsService userDetailsService(){
返回userDetailsService;
}
}
我需要为所有URI(http.authorizeRequests().antMatchers("/**").permitAll();
)的所有用户提供免费访问权限(不触发登录报价),但似乎为下一个注释@PreAuthorize
,@PreFilter
,@PostAuthorize
和@PostFilter
的触发处理程序受到了抑制。
我想我在这里误以http.authorizeRequests().antMatchers("/**").permitAll();
或其他配置部件。
更多问题症状:
从未调用CustomUserDetailsService.loadUserByUsername(..)
;
在REST API上,@AuthenticationPrincipal User activeUser
部分为null
在REST API上,Principal principal
部分也为null
最佳答案
可信空间问题具有与匿名用户标识类似的解决方案(在研究时,我已经得出了这个结论。)
简短答案
可信空间不需要授权,但是不会调用UserDetailsService,因为默认情况下仅使用AnonymousAuthenticationProvider
和AnonymousAuthenticationFilter
。足以实现基于AnonymousAuthenticationFilter
覆盖createAuthentication
的自定义过滤器,并用自定义1(AnonymousAuthenticationFilter
)替换默认值(CustomAnonymousAuthenticationFilter
):
@Configuration public static class NoAuthConfigurationAdapter extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Autowired private IdentifiableAnonymousAuthenticationFilter identifiableAnonymousAuthenticationFilter; @Override protected void configure(HttpSecurity http) throws Exception { http.anonymous().authenticationFilter(identifiableAnonymousAuthenticationFilter); http.antMatcher("/**").authorizeRequests() .anyRequest().permitAll(); } }
Full answer
I found out that CustomUserDetailsService will never be called if user is not authorized. Continuing research pay attention on the AnonymousAuthenticationFilter which is responsible for creating anonymous user info. So in the very and purpose is to replace the AnonymousAuthenticationFilter with my IdentifiableAnonymousAuthenticationFilter where some methods should be overridden:
@Component public class IdentifiableAnonymousAuthenticationFilter extends AnonymousAuthenticationFilter { public static final String KEY_IDENTIFIABLE_ANONYMOUS_AUTHENTICATION_FILTER = "Key.IdentifiableAnonymousAuthenticationFilter"; @Autowired private CustomUserDetailsService userDetailsService; @Autowired private GrantedAuthorityService grantedAuthorityService; private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); public IdentifiableAnonymousAuthenticationFilter() { this(KEY_IDENTIFIABLE_ANONYMOUS_AUTHENTICATION_FILTER); } public IdentifiableAnonymousAuthenticationFilter(String key) { super(key); } @Override protected Authentication createAuthentication(HttpServletRequest request) { AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken( KEY_IDENTIFIABLE_ANONYMOUS_AUTHENTICATION_FILTER, userDetailsService.loadCurrentUser(request), grantedAuthorityService.getGrantedAuthoritiesForCurrentUser()); auth.setDetails(authenticationDetailsSource.buildDetails(request)); return auth; } }
to inject it into configuration
@Configuration
public class IdentifyAnonymousConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Autowired
private IdentifiableAnonymousAuthenticationFilter identifiableAnonymousAuthenticationFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.anonymous().authenticationFilter(identifiableAnonymousAuthenticationFilter);
// ... some other configurations
}
}
现在看起来好多了,因为在AnonymousConfigurer中注入了identifiableAnonymousAuthenticationFilter。请注意基于
WebSecurityConfigurerAdapter
的配置。如果您只有几个,并且其中一个不会设置customAnonymousAuthenticationFilter,而是配置为早于custom ..,您将获得AnonymousAuthenticationFilter的默认实例(默认情况下在WebSecurityConfigurerAdapter
中配置):protected final HttpSecurity getHttp() throws Exception { //... http .csrf().and() .addFilter(new WebAsyncManagerIntegrationFilter()) .exceptionHandling().and() .headers().and() .sessionManagement().and() .securityContext().and() .requestCache().and() .anonymous().and() // ...
I would not care about it if application fixed, but AnonymousAuthenticationFilter called earlier than IdentifiableAnonymousAuthenticationFilter. And doFilter puts into SecurityContextHolder incorrect
Authentication.
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
if(SecurityContextHolder.getContext().getAuthentication() == null) {
SecurityContextHolder.getContext().setAuthentication(this.createAuthentication((HttpServletRequest)req));
if(this.logger.isDebugEnabled()) {
this.logger.debug("Populated SecurityContextHolder with anonymous token: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
}
} else if(this.logger.isDebugEnabled()) {
this.logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
}
chain.doFilter(req, res);
}
因此,下一次为IdentifiableAnonymousAuthenticationFilter调用doFilter时,由于条件
Authentication
,它不会替换if(SecurityContextHolder.getContext().getAuthentication() == null)
(请参见前面的方法)。因此,提供一种配置非常好,其中使用魔术批注@Order修复
WebSecurityConfigurerAdapter
配置以管理配置加载顺序。警告
或者有人会想-在没有条件的情况下在IdentifiableAnonymousAuthenticationFilter中添加
doFilter
覆盖(这是hack):@Override
公共无效doFilter(ServletRequest req,ServletResponse res,FilterChain链)
引发IOException,ServletException {
SecurityContextHolder.getContext()。setAuthentication(createAuthentication((HttpServletRequest)req));
如果(logger.isDebugEnabled()){
logger.debug(“带有匿名令牌的已填充SecurityContextHolder:'”
+ SecurityContextHolder.getContext()。getAuthentication()+“'”);
}
chain.doFilter(req,res);
}
如果您需要通过处理授权的/已认证的用户来确保弹簧安全性,那么这是不可接受的,但是在某些情况下,这就足够了。
附言
解决方案的某些部分可以改进,但我希望这个想法总体上是明确的。