为啥 OAuth2 身份验证服务器使用 jdbc 数据源返回 401 错误?
Posted
技术标签:
【中文标题】为啥 OAuth2 身份验证服务器使用 jdbc 数据源返回 401 错误?【英文标题】:Why does OAuth2 authentication server returns 401 error with jdbc datasource?为什么 OAuth2 身份验证服务器使用 jdbc 数据源返回 401 错误? 【发布时间】:2019-05-03 23:16:57 【问题描述】:我正在使用 JWT 实现 OAUTH2 身份验证服务器。 如果我使用 inMemory () 令牌,我可以正常访问。 但是,如果我使用 jdbc (dataSource),它总是返回错误 401。有人可以帮忙吗?
我的 AuthorizationServerConfigurerAdapter
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception
endpoints.tokenStore(tokenStore()).authenticationManager(authenticationManager)
.tokenEnhancer(jwtAccessTokenConverter()).userDetailsService(userDetailsService)
.requestFactory(customOauth2RequestFactory.requestFactory());
我的令牌商店
@Bean
public TokenStore tokenStore()
return new JwtTokenStore(jwtAccessTokenConverter());
还有我的 jwtAccessTokenConverter
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter()
var converteToken = new CustomToken();
converteToken.setKeyPair(new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "password".toCharArray())
.getKeyPair("jwt"));
return converteToken;
我的 CustomToken 扩展了 JwtAccessTokenConverter
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication)
var user = userRepository.findByLogin(authentication.getName());
Map<String, Object> additionalInformation = new HashMap<>()
put("idFuncionario", usuario.getIdFuncionario());
put("idEmpresa", usuario.getIdEmpresa());
put("perfis", usuario.descricaoPerfil());
put("login", usuario.getLogin());
;
var defaultOAuth2AccessToken = new DefaultOAuth2AccessToken(accessToken);
defaultOAuth2AccessToken.setAdditionalInformation(additionalInformation);
return super.enhance(defaultOAuth2AccessToken, authentication);
现在,我在 AuthorizationServerConfigurerAdapter 中的 JDBC
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
clients.jdbc(dataSource).passwordEncoder(passwordEncoder());
我的 CustomFactory 扩展了 DefaultOAuth2RequestFactory
@Override
public TokenRequest createTokenRequest(Map<String, String> requestParameters, ClientDetails authenticatedClient)
if (requestParameters.get("grant_type").equals("refresh_token"))
var authentication = tokenStore.readAuthenticationForRefreshToken(tokenStore.readRefreshToken(requestParameters
.get("refresh_token")));
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(
authentication.getName(), null, userDetailsService.loadUserByUsername(authentication.getName())
.getAuthorities()));
return super.createTokenRequest(requestParameters, authenticatedClient);
还有我的 JWT 实体
@Entity
@Table(name = "oauth_client_details")
public class OAuthClientDetails extends AbstractEntity
private String clientId;
private String clientSecret;
private String resourceIds;
private String scope;
private String authorizedGrantTypes;
private String webServerRedirectUri;
private String authorities;
private Integer accessTokenValidity;
private Integer refreshTokenValidity;
private String additionalInformation;
private String autoapprove;
contructor / geters / seters
【问题讨论】:
您使用什么密码编码器?您是否在数据库中加密了密码?使用用户名和密码显示您的数据库行。 【参考方案1】:我的解决方案:
我在一个扩展 AuthorizationServerConfigurerAdapter 的类中创建了这个方法
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
clients.withClientDetails(clientService);
还有这项服务:
@Service
public class ClientService implements ClientDetailsService
@Autowired
private ClienteRepository clienteRepository;
@Override
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException
var cliente = clienteRepository.findById(clientId);
var clienteDetail = new ClienteDetalhes(
cliente.get().getClientId(),
String.join(",", cliente.get().getResourceIds()),
cliente.get().getClientSecret(),
String.join(",", cliente.get().getScope()),
String.join(",", cliente.get().getAuthorizedGrantTypes()),
String.join(",", cliente.get().getRegisteredRedirectUri()),
null,
cliente.get().getAccessTokenValiditySeconds(),
cliente.get().getRefreshTokenValiditySeconds(),
cliente.get().getAutoApproveScope(),
null);
return clienteDetail;
还有这个实体
@Getter
@Setter
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(schema = "cliente")
public class ClienteDetalhes implements Serializable, ClientDetails
private static final ObjectMapper mapper = new ObjectMapper();
@Id
@Column(nullable = false, unique = true)
private String clientId;
private String resourceIds;
private String clientSecret;
private String scope;
@Column(nullable = false)
private String authorizedGrantTypes;
private String registeredRedirectUri;
private String authorities;
@Column(nullable = false)
private Integer accessTokenValiditySeconds;
@Column(nullable = false)
private Integer refreshTokenValiditySeconds;
private String autoApproveScope;
private String additionalInformation;
@Override
public boolean isSecretRequired()
return !StringUtils.isEmpty(this.clientSecret);
@Override
public boolean isAutoApprove(String scope)
return false;
@Override
public Map<String, Object> getAdditionalInformation()
try
return mapper.readValue(this.additionalInformation, Map.class);
catch (IOException e)
return new HashMap<>();
@Override
public Collection<GrantedAuthority> getAuthorities()
Set<String> set = StringUtils.commaDelimitedListToSet(this.authorities);
Set<GrantedAuthority> result = new HashSet<>();
set.forEach(authority -> result.add(new GrantedAuthority()
@Override
public String getAuthority()
return authority;
));
return result;
@Override
public Set<String> getRegisteredRedirectUri()
return StringUtils.commaDelimitedListToSet(this.registeredRedirectUri);
@Override
public Set<String> getAuthorizedGrantTypes()
return StringUtils.commaDelimitedListToSet(this.authorizedGrantTypes);
@Override
public boolean isScoped()
return this.getScope().size() > 0;
@Override
public Set<String> getScope()
return StringUtils.commaDelimitedListToSet(this.scope);
@Override
public Set<String> getResourceIds()
if (StringUtils.isEmpty(this.resourceIds))
return new HashSet<>();
else
return StringUtils.commaDelimitedListToSet(this.resourceIds);
现在一切正常!
【讨论】:
以上是关于为啥 OAuth2 身份验证服务器使用 jdbc 数据源返回 401 错误?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 Spring Security OAuth2 DefaultTokenServices 在加载身份验证时检查 clientId?
当我在授权标头中发送有效的不记名令牌时,为啥我的 Spring-Cloud Gateway / OAuth2-Client 没有通过身份验证?