Spring OAuth2-授权服务器-在客户端上区分用户

时间:2019-01-08 18:54:23

标签: spring spring-boot spring-oauth2

我被申请困扰。听起来很简单:我有两个在OAuth AuthorizationServer上注册的客户端和两个用户。用户 alpha 可以访问两个应用程序(“ androidapp”和“ angularapp”),但用户 beta 只能访问这些应用程序之一(仅“ angularapp”)。如何区分用户并阻止“ androidapp”应用的beta?

这是我的AuthServer的代码:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter{

    @Autowired private DataSource dataSource;
    @Autowired private AuthenticationManager authenticationManager;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
            .tokenKeyAccess("permitAll()")
            .checkTokenAccess("isAuthenticated()")
            ;
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient("angularapp")
        .secret(passwordEncoder.encode("12345"))
        .scopes("read", "write")
        .authorizedGrantTypes("password", "refresh_token")
        .accessTokenValiditySeconds(20000)
        .refreshTokenValiditySeconds(20000)
        .and()
        .withClient("androidapp")
        .secret(passwordEncoder.encode("67890"))
        .scopes("read", "write")
        .authorizedGrantTypes("password", "refresh_token")
        .accessTokenValiditySeconds(20000)
        .refreshTokenValiditySeconds(20000);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        endpoints
            .authenticationManager(authenticationManager)
            .tokenStore(tokenStore())
            .accessTokenConverter(accessTokenConverter())
            ;

    }

    @Bean
    public JwtTokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter jwt = new JwtAccessTokenConverter();
        jwt.setSigningKey(JwtConfig.RSA_PRIVATE_KEY);
        jwt.setVerifierKey(JwtConfig.RSA_PUBLIC_KEY);
        return jwt;
    }

}

预先感谢您的回答。

2 个答案:

答案 0 :(得分:1)

我在这里的解决方案:

  

执行loadClientByClientId方法时,Principal对象   存储在SecurityContext中的文件尚不存在,但当   仔细观察loadUserByUsername方法的执行:   此时的Principal对象包含client_id,而不是   username,从而自定义UserDetailsService对象   而不是ClientsDetailsService。然后,与关系实体(JPA)   我将client_idusername结合在一起,得到了预期的结果。

因此,UserDetailsService实现的代码是:

@Service
public class UsuarioService implements IUsuarioService, UserDetailsService{

    private Logger logger = LoggerFactory.getLogger(UsuarioService.class);

    @Autowired
    private IUsuarioDao usuarioDao;

    @Override
    @Transactional(readOnly=true)
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Usuario usuario = usuarioDao.findByUsername(username);
        if( usuario == null ) {
            logger.error("Login error: Username not found in storage");
            throw new UsernameNotFoundException("Login error: Username not found in storage");
        }
        List<GrantedAuthority> authorities = usuario.getRoles().stream().map( role -> new SimpleGrantedAuthority( role.getNombre() )).collect(Collectors.toList());

        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String applicationID = "";
        if (principal instanceof UserDetails) {
            applicationID = ((UserDetails)principal).getUsername();
        } else {
            applicationID = principal.toString();
        }
        logger.info("Application: {} ", applicationID);

        if( applicationID == null || applicationID.isEmpty() ) {
            logger.error("Application ID can't be empty");
            throw new InsufficientAuthenticationException("Application ID can't be empty");
        }

        OAuthClientDetails app = findApplicationByUsername( usuario.getClientes(), applicationID);
        if( app == null ) {
            logger.error("Unauthorized user for application {}", applicationID);
            throw new UnapprovedClientAuthenticationException("Unauthorized user for application " + applicationID);
        }

        return new User(username, usuario.getPassword(), usuario.getEnabled(), true, true, true, authorities);
    }

    private OAuthClientDetails findApplicationByUsername( final List<OAuthClientDetails> list, final String clientID ){
        return list.stream().filter( p -> p.getClientId().equals(clientID) ).findAny().orElse(null); } }

并且AuthorizationServer配置为:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter{

    @Autowired DataSource dataSource;
    @Autowired @Qualifier("authenticationManagerBean") private AuthenticationManager authenticationManager;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
            .tokenKeyAccess("permitAll()") 
            .checkTokenAccess("isAuthenticated()") 
            ;
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        endpoints
            .authenticationManager(authenticationManager)
            .tokenStore(tokenStore())
            .accessTokenConverter(accessTokenConverter())
            ;

    }

    @Bean
    public JwtTokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter jwt = new JwtAccessTokenConverter();
        jwt.setSigningKey(JwtConfig.RSA_PRIVATE_KEY);
        jwt.setVerifierKey(JwtConfig.RSA_PUBLIC_KEY);
        return jwt;
    }

}

非常感谢您的帮助和想法。

答案 1 :(得分:1)

我过去解决的方法是创建ResourceOwnerPasswordTokenGranter的子类并覆盖此方法:

protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {

如果您从Spring源代码中复制原始方法,则可以随时访问client_id(client.getClientId())和用户(userAuth.getPrincipal())。

如果用户的角色与客户端不匹配,我抛出InsufficientAuthenticationException以避免用户登录。

如果Spring Security在其中具有某种回调以避免复制代码的一部分以实现此目的,那将是很好的。我为此打开了https://github.com/spring-projects/spring-security-oauth/issues/791