java 使用Spring oauth2和keycloak实现多种身份验证方式

ss2ws0br  于 2022-12-28  发布在  Java
关注(0)|答案(1)|浏览(304)

我有一个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或仍然喜欢没有一个解决方案在头部。

7tofc5zh

7tofc5zh1#

我使用Keycloak来联合其他身份:客户端和资源服务器唯一信任的发布者是Keycloak示例或领域。2其他身份源隐藏在它后面。
使用该配置,角色由Keycloak放入令牌中,就像任何其他声明一样(对于我工作的上一个项目,角色引用是LDAP,但它可以是自定义数据库表或Keycloak默认表)。
将Keycloak连接到您的用户数据库非常容易,而且它还附带了许多我不想编码和维护的特性(多因素身份验证、社交登录、用户、客户端和资源服务器管理屏幕......)

相关问题