[英]How to dynamically change client_secret in Spring Security with Spring boot
現在我有這個配置:
spring:
security:
oauth2:
client:
registration:
sbbol:
client-id: zdcffffff
client-secret: ffffffffff
scope:
- openid
client-authentication-method: post
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
client-authentication-scheme: form
provider:
sbbol:
authorization-uri: ${SBBOL_AUTH_URI}
token-uri: ${SBBOL_AUTH_URI}
user-info-uri: ${SBBOL_AUTH_URI}
user-name-attribute: sub
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable();
http.cors().disable();
http.csrf().disable();
http.requestMatchers()
.antMatchers("/login", "/oauth2/authorization/sbbol", "/login/oauth2/code/sbbol")
.and()
.authorizeRequests().anyRequest().authenticated();
http.oauth2Login()
.defaultSuccessUrl("/user")
.permitAll();
}
}
這可行,但我的提供商要求我通過 rest api 調用每 30 天更改一次客戶端密碼。 我有一個問題,如何在 Spring Security 中設置新的客戶端密碼? 也許我可以將配置存儲在數據庫中?
我為 org.springframework.security.oauth2.client.registration.ClientRegistrationRepository 創建了自己的實現。 我可以將設置存儲在數據庫中並進行更改。
@Component
@RequiredArgsConstructor
public class JdbcClientRegistrationRepository implements ClientRegistrationRepository {
private final SsoProviderConfigurationRepository ssoProviderConfigurationRepository;
@Override
public ClientRegistration findByRegistrationId(String registrationId) {
Assert.hasText(registrationId, "registrationId cannot be empty");
SsoProviderConfiguration providerConfiguration = ssoProviderConfigurationRepository.findByRegistrationId(registrationId)
.orElseThrow(() -> new RuntimeException("ClientRegistration not found by id=" + registrationId));
String[] scopes = providerConfiguration.getScope().split(",");
return ClientRegistration.withRegistrationId(providerConfiguration.getRegistrationId())
.clientId(providerConfiguration.getClientId())
.clientSecret(providerConfiguration.getClientSecret())
.clientName(providerConfiguration.getClientName())
.authorizationGrantType(new AuthorizationGrantType(providerConfiguration.getAuthorizationGrantType()))
.authorizationUri(providerConfiguration.getAuthorizationUri())
.clientAuthenticationMethod(new ClientAuthenticationMethod(providerConfiguration.getClientAuthenticationMethod()))
.scope(scopes)
.tokenUri(providerConfiguration.getTokenUri())
.userInfoAuthenticationMethod(new AuthenticationMethod(providerConfiguration.getAuthenticationMethod()))
.userInfoUri(providerConfiguration.getUserInfoUri())
.userNameAttributeName(providerConfiguration.getUserNameAttributeName())
.redirectUri(providerConfiguration.getRedirectUri())
.build();
}
}
我的實體
@Entity
@Table(name = "sso_provider_configuration")
@Getter
@Setter
@NoArgsConstructor
public class SsoProviderConfiguration implements Serializable {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Long id;
private String registrationId;
private String clientId;
private String clientSecret;
private String clientAuthenticationMethod;
private String authorizationGrantType;
private String redirectUri;
private String scope;
private String clientName;
private String authorizationUri;
private String tokenUri;
private String jwkSetUri;
private String issuerUri;
private String authenticationMethod;
private String userNameAttributeName;
private String UserInfoUri;
}
存儲庫
public interface SsoProviderConfigurationRepository extends JpaRepository<SsoProviderConfiguration, Long> {
Optional<SsoProviderConfiguration> findByRegistrationId(String code);
}
我有這個可能有用,它不是動態的,但你可以解決
@Value("${security.oauth.client.id}")
private String clientId;
@Value("${security.oauth.client.password}")
private String clientPassword;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(clientId)
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.secret(clientPassword)
.resourceIds("resource_id")
.accessTokenValiditySeconds(accessTokenTimeOut)
.refreshTokenValiditySeconds(refreshTokenTimeOut)
;
}
我無法通過實現 ClientRegistrationRepository 創建 JdbcClientRegistrationRepository class。 它顯示它不可用,並且 ClientRegistration class 也不可用。 @Aleksandr Erokhin,如果您使用最新的 spring-security-oauth2-authorization-server:0.2.0 版本實現此場景,您能否分享代碼?
添加此配置 class:
import com.yourmom.security.oauth2.AppleClientSecretGenerator;
import com.yourmom.security.oauth2.ClientSecretGenerator;
import com.yourmom.service.DynamicInMemoryClientRegistrationRepository;
import java.util.ArrayList;
import java.util.HashMap;
import org.springframework.boot.autoconfigure.security.oauth2.client.ClientsConfiguredCondition;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesRegistrationAdapter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration(
proxyBeanMethods = false
)
@EnableConfigurationProperties({OAuth2ClientProperties.class})
@Conditional({ClientsConfiguredCondition.class})
class OAuthProps {
OAuthProps() {
}
@Bean
public DynamicInMemoryClientRegistrationRepository clientRegistrations(OAuth2ClientProperties properties,AppleClientSecretGenerator appleClientSecretGenerator) {
return new DynamicInMemoryClientRegistrationRepository(
new ArrayList(OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(properties).values()),
new HashMap<String, ClientSecretGenerator>(){{
put("apple",appleClientSecretGenerator);
}}
);
}
}
這個界面:
public interface ClientSecretGenerator {
public String generateClientSecret() throws Exception;
}
這個實現(如果你喜歡,它就是我使用它的目的)
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.io.File;
import java.io.FileReader;
import java.security.PrivateKey;
import java.util.Date;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;
@Service
public class AppleClientSecretGenerator implements ClientSecretGenerator {
@Value("${spring.security.oauth2.client.registration.apple.keyId}")
private String appleKeyId;
@Value("${spring.security.oauth2.client.registration.apple.teamId}")
private String appleTeamId;
@Value("${spring.security.oauth2.client.registration.apple.clientId}")
private String clientId;
@Override
public String generateClientSecret() throws Exception {
// Generate a private key for token verification from your end with your creds
PrivateKey pKey = generatePrivateKey();
return Jwts.builder()
.setHeaderParam(JwsHeader.KEY_ID, appleKeyId)
.setIssuer(appleTeamId)
.setAudience("https://appleid.apple.com")
.setSubject(clientId)
.setExpiration(new Date(System.currentTimeMillis() + (1000 * 60 * 500)))
.setIssuedAt(new Date(System.currentTimeMillis()))
.signWith(SignatureAlgorithm.ES256,pKey)
.compact();
}
private PrivateKey generatePrivateKey() throws Exception {
// here i have added cert at resource/apple folder. So if you have added somewhere else, just replace it with your path ofcert
File file = ResourceUtils.getFile("classpath:security/oauth/apple/AuthKey_"+appleKeyId+".p8");
final PEMParser pemParser = new PEMParser(new FileReader(file));
final JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
final PrivateKeyInfo object = (PrivateKeyInfo) pemParser.readObject();
final PrivateKey pKey = converter.getPrivateKey(object);
pemParser.close();
return pKey;
}
}
最后是這個ClientRegistrationRepository
:
import com.yourmom.security.oauth2.ClientSecretGenerator;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.util.Assert;
public class DynamicInMemoryClientRegistrationRepository implements ClientRegistrationRepository, Iterable<ClientRegistration> {
private final Map<String, ClientRegistration> registrations;
private final Map<String, ClientSecretGenerator> clientSecretGeneratorMap;
public DynamicInMemoryClientRegistrationRepository(List<ClientRegistration> clientRegistrations,Map<String, ClientSecretGenerator> clientSecretGeneratorMap) {
this.clientSecretGeneratorMap = clientSecretGeneratorMap;
registrations = createRegistrationsMap(clientRegistrations);
}
private static Map<String, ClientRegistration> createRegistrationsMap(List<ClientRegistration> registrations) {
Assert.notEmpty(registrations, "registrations cannot be empty");
return toUnmodifiableConcurrentMap(registrations);
}
private static Map<String, ClientRegistration> toUnmodifiableConcurrentMap(List<ClientRegistration> registrations) {
ConcurrentHashMap<String, ClientRegistration> result = new ConcurrentHashMap();
Iterator var2 = registrations.iterator();
while(var2.hasNext()) {
ClientRegistration registration = (ClientRegistration)var2.next();
Assert.state(!result.containsKey(registration.getRegistrationId()), () -> {
return String.format("Duplicate key %s", registration.getRegistrationId());
});
result.put(registration.getRegistrationId(), registration);
}
return Collections.unmodifiableMap(result);
}
public ClientRegistration findByRegistrationId(String registrationId) {
Assert.hasText(registrationId, "registrationId cannot be empty");
ClientRegistration immutableClientRegistration = this.registrations.get(registrationId);
try {
return ClientRegistration
.withRegistrationId(registrationId)
.clientId(immutableClientRegistration.getClientId())
.authorizationGrantType(immutableClientRegistration.getAuthorizationGrantType())
.clientName(immutableClientRegistration.getClientName())
.clientAuthenticationMethod(immutableClientRegistration.getClientAuthenticationMethod())
.clientSecret(clientSecretGeneratorMap.get(registrationId) != null ? clientSecretGeneratorMap.get(registrationId).generateClientSecret() : immutableClientRegistration.getClientSecret())
.redirectUri(immutableClientRegistration.getRedirectUri())
.scope(immutableClientRegistration.getScopes())
.providerConfigurationMetadata(immutableClientRegistration.getProviderDetails().getConfigurationMetadata())
.issuerUri(immutableClientRegistration.getProviderDetails().getIssuerUri())
.jwkSetUri(immutableClientRegistration.getProviderDetails().getJwkSetUri())
.tokenUri(immutableClientRegistration.getProviderDetails().getTokenUri())
.authorizationUri(immutableClientRegistration.getProviderDetails().getAuthorizationUri())
.userInfoUri(immutableClientRegistration.getProviderDetails().getUserInfoEndpoint().getUri())
.userInfoAuthenticationMethod(immutableClientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod())
.userNameAttributeName(immutableClientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName())
.build();
} catch (Exception e) {
throw new RuntimeException("Failed while generating client secret: " + e,e);
}
}
public Iterator<ClientRegistration> iterator() {
return this.registrations.values().iterator();
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.