我有一个Sping Boot 项目,它使用spring oauth2令牌提供程序运行身份验证。现在有一个想法是使用Keycloak支持身份验证,这样用户名和密码将存储在Keycloak中,它将提供访问令牌。想法是保持oauth令牌存储和提供程序,以及有一个keycloak。但是为了保持角色和访问权限是Spring的一部分,Keycloak将只用于一些用户作为令牌提供者而不是Spring,并具有刷新令牌。
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private ClientDetailsService clientDetailsService;
private AccessDecisionManager accessDecisionManager;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().accessDecisionManager(accessDecisionManager)
.antMatchers("/service/*").fullyAuthenticated()
.anyRequest().permitAll().and().httpBasic().and().csrf().disable();
}
@Override
@Bean(name = "authenticationManagerBean")
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore) {
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
@Bean
@Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
@Bean
public AffirmativeBased accessDecisionManager() {
List<AccessDecisionVoter<?>> accessDecisionVoters = new ArrayList<>();
accessDecisionVoters.add(new ScopeVoter());
accessDecisionVoters.add(new RoleVoter());
accessDecisionVoters.add(new AuthenticatedVoter());
AffirmativeBased accessDecisionManager = new AffirmativeBased(accessDecisionVoters);
return accessDecisionManager;
}
还有一个自定义客户端服务,用于赠款访问权限:
@Component
public class CustomClientService implements ClientDetailsService {
private static Map<String, BaseClientDetails> cache = new ConcurrentHashMap<>();
@Autowired
UserService userService;
@Autowired
AccessRightsService accessRightsService;
@Override
public ClientDetails loadClientByClientId(String paramString) throws ClientRegistrationException {
...
此外,还有一个自定义TokenStore类:
public class MyTokenServices extends DefaultTokenServices {
private static Logger log = LoggerFactory.getLogger(MyTokenServices.class);
public UserService userService;
public AccessRightsService accessRightService;
private TokenStore my_tokenStore;
@Override
public void setTokenStore(TokenStore tokenStore) {
// TODO Auto-generated method stub
super.setTokenStore(tokenStore);
my_tokenStore = tokenStore;
}
@Override
@Transactional
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
OAuth2AccessToken retVal= super.createAccessToken(authentication);
if(retVal instanceof DefaultOAuth2AccessToken) {
DefaultOAuth2AccessToken defRetVal = (DefaultOAuth2AccessToken)retVal;
log.info("New loging request"+ defRetVal.toString());
// defRetVal.setExpiration( Date.from(LocalDateTime.now().plus(8,ChronoUnit.HOURS).atZone(ZoneId.systemDefault()).toInstant()));
my_tokenStore.storeAccessToken(defRetVal, authentication);
}
return retVal;
}
@Override
public OAuth2Authentication loadAuthentication(String accessTokenValue)
throws AuthenticationException, InvalidTokenException {
OAuth2Authentication retVal = super.loadAuthentication(accessTokenValue);
OAuth2Request oldRequest = retVal.getOAuth2Request();
User user = userService.getUserByUsername(oldRequest.getClientId());
if(changeAutheticator(retVal, user)) {
HashSet<GrantedAuthority> authorities = new HashSet<>();
user.getRoles().forEach(a->authorities.add(new SimpleGrantedAuthority(a.getRoleName())));
Set<String> accessRights = accessRightService.getUserAccessRights(user);
if(accessRights != null) {
accessRights.forEach(rihgt->{
authorities.add(new SimpleGrantedAuthority(rihgt));
});
}
OAuth2Request newRequest = new OAuth2Request(retVal.getOAuth2Request().getRequestParameters(),
oldRequest.getClientId(), authorities, oldRequest.isApproved(), oldRequest.getScope(),
oldRequest.getResourceIds(), oldRequest.getRedirectUri(), oldRequest.getResponseTypes(), oldRequest.getExtensions());
retVal = new OAuth2Authentication(newRequest, retVal.getUserAuthentication());
}
return retVal;
}
/**
* Method that check do we need to change authenticator
* @param retVal
* @param user
* @return
*/
private boolean changeAutheticator(OAuth2Authentication auth, User user) {
if(user == null) return false;
if(user != null ) {
if(user.getRoles() != null) {
if(auth.getOAuth2Request()!=null && auth.getOAuth2Request().getAuthorities() != null){
for(Role role:user.getRoles()){
if(!auth.getOAuth2Request().getAuthorities().stream().anyMatch(a->a.getAuthority().equals(role.getRoleName()))){
return true;
}
}
for(GrantedAuthority ga : auth.getOAuth2Request().getAuthorities()) {
if(!user.getRoles().stream().anyMatch(a->a.getRoleName().equals(ga.getAuthority()))){
return true;
}
}
}
}
}
return false;
}
我试图实现一个多重身份验证,就像来自其他stackoverflow的一个,但这不是一个解决方案。认为我应该提供一个自定义的身份验证提供程序与Keycloak或仍然喜欢没有一个解决方案在头部。
1条答案
按热度按时间7tofc5zh1#
我使用Keycloak来联合其他身份:客户端和资源服务器唯一信任的发布者是Keycloak示例或领域。2其他身份源隐藏在它后面。
使用该配置,角色由Keycloak放入令牌中,就像任何其他声明一样(对于我工作的上一个项目,角色引用是LDAP,但它可以是自定义数据库表或Keycloak默认表)。
将Keycloak连接到您的用户数据库非常容易,而且它还附带了许多我不想编码和维护的特性(多因素身份验证、社交登录、用户、客户端和资源服务器管理屏幕......)