我正在使用Spring Boot(2.3.4.RELEASE)来实现充当OAuth2资源服务器的Web服务。到目前为止,我已经能够保护所有端点并确保存在有效的令牌。在下一步中,我想使用Spring Method Security。第三步是填充自定义用户详细信息(通过$con->bindParam(':keyword',$keyword,PDO::PARAM_STR);
)。
我无法(正确)启用Spring Method Security。我有保存在数据库中的实体,还通过MutableAclService设置了权限。创建新资源没问题。
我阅读实体
时遇到以下错误UserDetailsService
我使用以下表达式:
o.s.s.acls.AclPermissionEvaluator : Checking permission 'OWNER' for object 'org.springframework.security.acls.domain.ObjectIdentityImpl[Type: io.mvc.webserver.repository.entity.ProjectEntity; Identifier: my-second-project]'
o.s.s.acls.AclPermissionEvaluator : Returning false - no ACLs apply for this principal
o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter@120d62d, returned: -1
o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.access.vote.RoleVoter@429b9eb9, returned: 0
o.s.s.access.vote.AffirmativeBased : Voter: org.springframework.security.access.vote.AuthenticatedVoter@65342bae, returned: 0
o.s.web.servlet.DispatcherServlet : Failed to complete request: org.springframework.security.access.AccessDeniedException: Zugriff verweigert
o.s.s.w.a.ExceptionTranslationFilter : Access is denied (user is not anonymous); delegating to AccessDeniedHandler
据我所知,Spring Security会根据身份验证的用户(通过OAuth2 JWT)设置SecurityContext。我想基于从令牌中识别出的用户设置自定义用户对象(主要)。但是仅提供类型@PreAuthorize("hasPermission(#projectKey, 'io.mvc.webserver.repository.entity.ProjectEntity', 'OWNER')")
ProjectEntity findByKey(String projectKey);
的Bean似乎无效。我的UserDetailsService永远不会被调用...
安全配置
UserDetailsService
ACL配置
@Configuration
@EnableWebSecurity
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.cors().and()
.httpBasic().disable()
.formLogin().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests(authorize -> authorize
.antMatchers("/actuator/**").permitAll() // TODO: Enable basic auth for actuator
.anyRequest().authenticated()
)
.oauth2ResourceServer().jwt();
}
}
依赖项
@Configuration
public class AclConfiguration {
@Bean
public MethodSecurityExpressionHandler methodSecurityExpressionHandler(PermissionEvaluator permissionEvaluator) {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(permissionEvaluator);
return expressionHandler;
}
@Bean
public PermissionEvaluator permissionEvaluator(PermissionFactory permissionFactory, AclService aclService) {
AclPermissionEvaluator permissionEvaluator = new AclPermissionEvaluator(aclService);
permissionEvaluator.setPermissionFactory(permissionFactory);
return permissionEvaluator;
}
@Bean
public PermissionFactory permissionFactory() {
return new DefaultPermissionFactory(MvcPermission.class);
}
@Bean
public MutableAclService aclService(LookupStrategy lookupStrategy, AclCache aclCache, AclRepository aclRepository) {
return new MongoDBMutableAclService(aclRepository, lookupStrategy, aclCache);
}
@Bean
public AclAuthorizationStrategy aclAuthorizationStrategy() {
return new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_ADMIN"));
}
@Bean
public PermissionGrantingStrategy permissionGrantingStrategy() {
return new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());
}
@Bean
public AclCache aclCache(PermissionGrantingStrategy permissionGrantingStrategy,
AclAuthorizationStrategy aclAuthorizationStrategy,
EhCacheFactoryBean ehCacheFactoryBean) {
return new EhCacheBasedAclCache(
ehCacheFactoryBean.getObject(),
permissionGrantingStrategy,
aclAuthorizationStrategy
);
}
@Bean
public EhCacheFactoryBean aclEhCacheFactoryBean(EhCacheManagerFactoryBean ehCacheManagerFactoryBean) {
EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
ehCacheFactoryBean.setCacheManager(ehCacheManagerFactoryBean.getObject());
ehCacheFactoryBean.setCacheName("aclCache");
return ehCacheFactoryBean;
}
@Bean
public EhCacheManagerFactoryBean aclCacheManager() {
EhCacheManagerFactoryBean cacheManagerFactory = new EhCacheManagerFactoryBean();
cacheManagerFactory.setShared(true);
return cacheManagerFactory;
}
@Bean
public LookupStrategy lookupStrategy(MongoTemplate mongoTemplate,
AclCache aclCache,
AclAuthorizationStrategy aclAuthorizationStrategy) {
return new BasicMongoLookupStrategy(
mongoTemplate,
aclCache,
aclAuthorizationStrategy,
new ConsoleAuditLogger()
);
}
}
答案 0 :(得分:0)
在ResourceServerConfig类中,您应该重写configureGlobal和authenticationManagerBean方法,并提供passwordEncoderBean以便调用userDeatailsService:
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoderBean());
}
@Bean
public PasswordEncoder passwordEncoderBean() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
configureGlobal中的变量userDetailsService应该持有对org.springframework.security.core.userdetails.UserDetailsService的实现的引用(通过类中的依赖注入@Autowird),在实现中,您应该重写方法loasUserByUsername来获取数据库中的实际用户,并将所需的值传递给UserDetails用户,身份验证管理器将使用该用户或Principal:
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<UserFromDb> user = userRepository.findByUsername(username);
if (!user.isPresent()) {
throw new UsernameNotFoundException("User not found!");
}
return new MyUser(user.get());
}
MyUser类应实现org.springframework.security.core.userdetails.UserDetails并将所需值传递给MyUser,如示例所示。如何传递所需的值取决于您,在这里,我从数据库传递了用户,并在实现中从内部提取了所需的任何值。
您应将以下行添加到configure方法的末尾
http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
authenticationTokenFilter是实现OncePerRequestFilter的类型,您应该覆盖方法doFilterInternal:
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, FilterChain filterChain)
throws ServletException, IOException {
final String requestTokenHeader = httpServletRequest.getHeader("Authorization");//sometime it's lowercase: authorization
String username = getUserName(requestTokenHeader);
String jwtToken = getJwtToken(requestTokenHeader);
if (username != null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (isValidToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
当然,您应该编写getUserName,getJwtToken和isValidToken方法的逻辑,这需要了解JWT令牌和http标头...