为可信空间定制Spring Security [英] Customize Spring Security for trusted space
问题描述
服务在可信空间中的网关之后工作(gateWay验证OAuth令牌并仅向服务提供唯一的用户ID,其他情况下重定向以验证服务)。
Service works after gateway in trusted space (gateWay verifies OAuth token and gives to the service only unique user ID other case it redirects to authenticate service).
我想要在服务中使用spring security可以验证userId的权限。
I want use spring security in the service to be able validate permissions for 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和 grantedAuthorityService.getGrantedAuthoritiesForCurrentUser()返回权限。
Where contextSsoActiveProfileIdProvider.getSsoActiveProfileId() returns uniqueUserId and grantedAuthorityService.getGrantedAuthoritiesForCurrentUser() returns authorities.
该服务在受信任区域中启动,因此我已通过下一步方式配置安全性:
The service starts in trusted zone so I have configured security in next way:
@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
。
I need provide free access for all users (without triggering login offer) for all URIs (http.authorizeRequests().antMatchers("/**").permitAll();
) but it seems suppressed triggering handlers for next annotations @PreAuthorize
, @PreFilter
, @PostAuthorize
and @PostFilter
.
我想我在这里误以为 http.authorizeRequests()。antMatchers(/ **)。permitAll( );
或与其他配置部分。
I suppose I mistook here with http.authorizeRequests().antMatchers("/**").permitAll();
or with other configuration part.
更多问题症状:
-
CustomUserDetailsService.loadUserByUsername永远不会调用(..)
; - 在REST API部分
@AuthenticationPrincipal用户activeUser
为空 - 在REST API部分
Principal principal
为null
CustomUserDetailsService.loadUserByUsername(..)
is never called;- On REST API part
@AuthenticationPrincipal User activeUser
is null - On REST API part
Principal principal
is null also
推荐答案
可信空间问题与匿名用户识别有类似的解决方案(我在处理它时已经做了这个结论。)
Trusted space issue has similar solution to anonymous user identification (I've done this conclusion when I was working on it.)
可信空间不需要授权,但不会调用 UserDetailsService ,因为只使用 AnonymousAuthenticationProvider
和 AnonymousAuthenticationFilter
默认情况下。基于 AnonymousAuthenticationFilter
重写 createAuthentication
并替换默认值( AnonymousAuthenticationFilter
)自定义一个( CustomAnonymousAuthenticationFilter
):
Trusted space does not need authorization, but no UserDetailsService will be called, because of using only AnonymousAuthenticationProvider
and AnonymousAuthenticationFilter
by default. It is good enough implement custom filter based on AnonymousAuthenticationFilter
overriding createAuthentication
and replace default (AnonymousAuthenticationFilter
) with custom one (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();
}
}
完整答案
I发现如果用户未获得授权,将永远不会调用 CustomUserDetailsService 。持续研究关注 AnonymousAuthenticationFilter ,它负责创建匿名用户信息。因此,目的是用我的 IdentifiableAnonymousAuthenticationFilter 替换 AnonymousAuthenticationFilter ,其中应该覆盖某些方法:
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;
}
}
将其注入配置
@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
:
Now it seems much better, because identifiableAnonymousAuthenticationFilter is injected in AnonymousConfigurer. Pay your attention to your configurations based on WebSecurityConfigurerAdapter
. If you have few ones and one of them will not set customAnonymousAuthenticationFilter but configured earlier than custom.. you'll get default instance of AnonymousAuthenticationFilter (configured in WebSecurityConfigurerAdapter
by default):
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 放入 SecurityContextHolder 不正确
身份验证。
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);
}
因此,当下次 doFilter 被调用 IdentifiableAnonymousAuthenticationFilter 由于条件 if(SecurityContextHolder.getContext()。getAuthentication()== null),它不会替换
(参见之前的方法)。身份验证
So when next time doFilter is called for IdentifiableAnonymousAuthenticationFilter it does not replace the Authentication
because of condition if(SecurityContextHolder.getContext().getAuthentication() == null)
(see method before).
结果非常适合提供修复 WebSecurityConfigurerAdapter $的配置c $ c>使用魔术注释配置 @Order 来管理配置加载顺序。
As result would be really good to provide configuration where fix for WebSecurityConfigurerAdapter
configuration using magic annotation @Order to manage configuration loading order.
或者有人会想 - 在没有条件的情况下添加 doFilter
覆盖 IdentifiableAnonymousAuthenticationFilter ( hack ):
Or someone could think - add doFilter
overriding in IdentifiableAnonymousAuthenticationFilter without condition (it is 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);
}
如果你需要Spring安全性来处理授权/认证用户,这是不可接受的,但在某些情况下它就足够了。
It is not acceptable if you need spring security with handling authorized/authenticated user but in some cases it is enough.
解决方案的某些部分可以改进,但我希望这个想法很清楚。
Some parts of the solution could be improved but I hope that idea is clear in general.
这篇关于为可信空间定制Spring Security的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!