[英]Customize Spring Security for trusted space
服务在网关在受信任空间中运行之后(gateWay验证OAuth令牌,并仅向服务提供唯一的用户ID,否则它将重定向到身份验证服务)。
我想在服务中使用Spring Security来验证userId的权限。
所以我添加了CustomUserDetailsService
@Service("userDetailsService") public class CustomUserDetailsService implements UserDetailsService { @Autowired(required = false) private ContextSsoActiveProfileIdProvider contextSsoActiveProfileIdProvider; @Autowired private GrantedAuthorityService grantedAuthorityService; @Override public User loadUserByUsername(final String username) throws UsernameNotFoundException { // verify it with authentication service, but there is not token, userId only, so trust to gateway service. return new User( String.valueOf(contextSsoActiveProfileIdProvider.getSsoActiveProfileId()), "authenticatedWithGateWay", grantedAuthorityService.getGrantedAuthoritiesForCurrentUser() ); } }
其中contextSsoActiveProfileIdProvider.getSsoActiveProfileId()返回uniqueUserId, grantAuthorityService.getGrantedAuthoritiesForCurrentUser()返回权限。
该服务在受信任区域中启动,因此我已通过以下方式配置了安全性:
@EnableWebSecurity @Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/**").permitAll(); } @Override protected UserDetailsService userDetailsService() { return userDetailsService; } }
我需要为所有URI( http.authorizeRequests().antMatchers("/**").permitAll();
)的所有用户提供免费访问权限(不触发登录报价),但是似乎已取消触发下一个批注@PreAuthorize
处理程序, @PreFilter
, @PostAuthorize
和@PostFilter
。
我想我在这里误以为http.authorizeRequests().antMatchers("/**").permitAll();
或其他配置部分。
更多问题症状:
CustomUserDetailsService.loadUserByUsername(..)
; @AuthenticationPrincipal User activeUser
为null Principal principal
也为null 可信空间问题具有与匿名用户标识类似的解决方案(在研究时,我已经得出了这个结论。)
可信空间不需要授权,但是不会调用UserDetailsService ,因为默认情况下仅使用AnonymousAuthenticationProvider
和AnonymousAuthenticationFilter
。 足以实现基于AnonymousAuthenticationFilter
自定义过滤器,该过滤器将覆盖createAuthentication
并用自定义项( CustomAnonymousAuthenticationFilter
)替换默认值( AnonymousAuthenticationFilter
):
@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(); } }
我发现,如果未授权用户,则永远不会调用CustomUserDetailsService 。 继续进行的研究集中在负责创建匿名用户信息的AnonymousAuthenticationFilter上。 因此,最主要的目的是用我的IdentifiableAnonymousAuthenticationFilter替换AnonymousAuthenticationFilter ,其中应重写某些方法:
@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; } }
将其注入配置
@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
}
}
现在看起来好多了,因为identifiableAnonymousAuthenticationFilter已注入AnonymousConfigurer中 。 请注意基于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() // ...
如果应用程序已修复,我不会在乎,但是AnonymousAuthenticationFilter早于IdentifiableAnonymousAuthenticationFilter调用。 并且doFilter将incorrect
身份验证放入SecurityContextHolder 。
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时,由于条件if(SecurityContextHolder.getContext().getAuthentication() == null)
(请参见之前的方法),它不会替换Authentication
。
因此,提供配置非常好,其中使用魔术批注@Order修复WebSecurityConfigurerAdapter
配置,以管理配置加载顺序。
或者有人会想-在没有条件的情况下在IdentifiableAnonymousAuthenticationFilter中添加覆盖doFilter
(这是hack ):
@Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { SecurityContextHolder.getContext().setAuthentication(createAuthentication((HttpServletRequest) req)); if (logger.isDebugEnabled()) { logger.debug("Populated SecurityContextHolder with anonymous token: '" + SecurityContextHolder.getContext().getAuthentication() + "'"); } chain.doFilter(req, res); }
如果您需要通过处理授权的/已认证的用户来确保弹簧安全性,那么这是不可接受的,但是在某些情况下,这就足够了。
解决方案的某些部分可以改进,但我希望这个想法总体上是明确的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.