我被申请困扰。听起来很简单:我有两个在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;
}
}
预先感谢您的回答。
答案 0 :(得分:1)
我在这里的解决方案:
执行
loadClientByClientId
方法时,Principal
对象 存储在SecurityContext
中的文件尚不存在,但当 仔细观察loadUserByUsername
方法的执行: 此时的Principal
对象包含client_id
,而不是username
,从而自定义UserDetailsService
对象 而不是ClientsDetailsService
。然后,与关系实体(JPA) 我将client_id
与username
结合在一起,得到了预期的结果。
因此,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。