spring-security-oauth2-authorization-server + angular-auth-oidc-client
spring-security-oauth2-authorization-server + angular-auth-oidc-client
我正在使用
"angular-auth-oidc-client": "^13.1.0",
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>0.2.2</version>
</dependency>
我在我的应用程序中使用“OpenId Connect”。如果我使用 Keycloak,我的应用程序运行良好。我喜欢将其替换为 spring-security-oauth2-authorization-server。我遇到了一些我无法解决的问题。
我点击我的应用程序,然后我被重定向到登录 UI。我输入 User + PW 并表示同意。然后我再次发回我的应用程序。在这里我看到以下问题
“this.storagePersistenceService”中没有匹配的数据。
你有什么想法可能是错误的吗
此致
G
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.List;
import java.util.UUID;
/**
* @author Joe Grandja
* @since 0.0.1
*/
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfigNew {
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("*"));
configuration.setAllowedMethods(List.of("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.formLogin(Customizer.withDefaults()).cors().configurationSource(corsConfigurationSource()).and().build();
}
// @formatter:off
@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("medical-share-openid-connect-client-id")
.clientSecret("{noop}secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri("http://127.0.0.1:4200/")
.redirectUri("http://127.0.0.1:4200")
.redirectUri("http://localhost:4200/")
// .redirectUri("http://127.0.0.1:4200/login/oauth2/code/messaging-client-oidc")
// .redirectUri("http://127.0.0.1:4200/authorized")
.scope(OidcScopes.OPENID)
.scope("offline_access")
// .scope("message.read")
// .scope("message.write")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
// Save registered client in db as if in-memory
JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
registeredClientRepository.save(registeredClient);
return registeredClientRepository;
}
// @formatter:on
@Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
}
@Bean
public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = Jwks.generateRsa();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
@Bean
public ProviderSettings providerSettings() {
// return ProviderSettings.builder().issuer("http://auth-server:9000").build();
return ProviderSettings.builder().issuer("http://localhost:8088").build();
}
@Bean
public EmbeddedDatabase embeddedDatabase() {
// @formatter:off
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(EmbeddedDatabaseType.H2)
.setScriptEncoding("UTF-8")
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql")
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql")
.addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql")
.build();
// @formatter:on
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import static org.springframework.security.config.Customizer.withDefaults;
@EnableWebSecurity
public class DefaultSecurityConfigNew {
// @formatter:off
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated()
)
.formLogin(withDefaults());
return http.build();
}
// @formatter:on
// @formatter:off
@Bean
UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("U-294b70ca67df4c018f59950f35314944")
.password("U-294b70ca67df4c018f59950f35314944")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
// @formatter:on
}
/*
* Copyright 2020-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jose.jwk.ECKey;
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.nimbusds.jose.jwk.RSAKey;
import javax.crypto.SecretKey;
import java.security.KeyPair;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;
/**
* @author Joe Grandja
* @since 0.1.0
*/
public final class Jwks {
private Jwks() {
}
public static RSAKey generateRsa() {
KeyPair keyPair = KeyGeneratorUtils.generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// @formatter:off
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
// @formatter:on
}
public static ECKey generateEc() {
KeyPair keyPair = KeyGeneratorUtils.generateEcKey();
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
Curve curve = Curve.forECParameterSpec(publicKey.getParams());
// @formatter:off
return new ECKey.Builder(curve, publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
// @formatter:on
}
public static OctetSequenceKey generateSecret() {
SecretKey secretKey = KeyGeneratorUtils.generateSecretKey();
// @formatter:off
return new OctetSequenceKey.Builder(secretKey)
.keyID(UUID.randomUUID().toString())
.build();
// @formatter:on
}
}
/*
* Copyright 2020-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
/**
* @author Joe Grandja
* @since 0.1.0
*/
final class KeyGeneratorUtils {
private KeyGeneratorUtils() {
}
static SecretKey generateSecretKey() {
SecretKey hmacKey;
try {
hmacKey = KeyGenerator.getInstance("HmacSha256").generateKey();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return hmacKey;
}
static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
static KeyPair generateEcKey() {
EllipticCurve ellipticCurve = new EllipticCurve(
new ECFieldFp(
new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951")),
new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853948"),
new BigInteger("41058363725152142129326129780047268409114441015993725554835256314039467401291"));
ECPoint ecPoint = new ECPoint(
new BigInteger("48439561293906451759052585252797914202762949526041747995844080717082404635286"),
new BigInteger("36134250956749795798585127919587881956611106672985015071877198253568414405109"));
ECParameterSpec ecParameterSpec = new ECParameterSpec(
ellipticCurve,
ecPoint,
new BigInteger("115792089210356248762697446949407573529996955224135760342422259061068512044369"),
1);
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
keyPairGenerator.initialize(ecParameterSpec);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
}
import {NgModule} from '@angular/core';
import {AuthModule, LogLevel} from 'angular-auth-oidc-client';
@NgModule({
imports: [
AuthModule.forRoot({
config: {
configId: 'ms-angular-auth-oidc-client-lib-id',
logLevel: LogLevel.Debug,
historyCleanupOff: false,
authority: 'http://localhost:8088',
redirectUrl: 'http://127.0.0.1:4200' + '/',
postLogoutRedirectUri: 'http://127.0.0.1:4200' + '/',
clientId: 'medical-share-openid-connect-client-id',
scope: 'openid offline_access',
responseType: 'code',
silentRenew: true,
renewTimeBeforeTokenExpiresInSeconds: 30,
ignoreNonceAfterRefresh: true,
useRefreshToken: true,
autoUserInfo: false, // change that as it is in the example for spring security like this
secureRoutes: [
'http://localhost:4200/',
'localhost:4200/',
'127.0.0.1:4200/',
'http://127.0.0.1:4200/',
'http://localhost:4200/',
'http://localhost:8081',
'localhost:8081',
],
},
}),
],
exports: [AuthModule],
})
export class AuthConfigModule {}
09:38:13.475 Navigated to http://localhost:4200/should-login
09:38:13.682 [webpack-dev-server] Live Reloading enabled. index.js:548
09:38:13.875 [DEBUG] ms-angular-auth-oidc-client-lib-id - Did not find any configured route for route http://localhost:8088/.well-known/openid-configuration angular-auth-oidc-client.mjs:160:20
09:38:13.878 [DEBUG] ms-angular-auth-oidc-client-lib-id - Did not find any configured route for route ../../assets/config/config.json angular-auth-oidc-client.mjs:160:20
09:38:13.956 Angular is running in development mode. Call enableProdMode() to enable production mode. core.mjs:24856:16
09:38:13.967 [DEBUG] ms-angular-auth-oidc-client-lib-id - Working with config 'ms-angular-auth-oidc-client-lib-id' using http://localhost:8088 angular-auth-oidc-client.mjs:160:20
09:38:13.968 [DEBUG] ms-angular-auth-oidc-client-lib-id - currentUrl to check auth with: http://localhost:4200/should-login angular-auth-oidc-client.mjs:157:20
09:38:13.969 [DEBUG] ms-angular-auth-oidc-client-lib-id - checkAuth completed - firing events now. isAuthenticated: false angular-auth-oidc-client.mjs:160:20
09:38:13.970 Navigated to http://localhost:8088/oauth2/authorize?client_id=medical-share-openid-connect-client-id&redirect_uri=http%3A%2F%2F127.0.0.1%3A4200%2F&response_type=code&scope=openid%20offline_access&nonce=e9bd6b927b16552d4560ef708d5fd8ca49mFmzaxQ&state=771502df2eae52657faf84e1e9cf3253b3TN7VCvc&code_challenge=0AggJb7Z2ONJmNU7rAqXh9BmKf5YeEcrLlmw9lgoMJE&code_challenge_method=S256
09:38:13.970 [DEBUG] ms-angular-auth-oidc-client-lib-id - BEGIN Authorize OIDC Flow, no auth data angular-auth-oidc-client.mjs:160:20
09:38:13.971 [DEBUG] ms-angular-auth-oidc-client-lib-id - Nonce created. nonce:e9bd6b927b16552d4560ef708d5fd8ca49mFmzaxQ angular-auth-oidc-client.mjs:160:20
09:38:13.971 [DEBUG] ms-angular-auth-oidc-client-lib-id - Authorize created. adding myautostate: 771502df2eae52657faf84e1e9cf3253b3TN7VCvc angular-auth-oidc-client.mjs:160:20
09:38:13.994 downloadable font: download failed (font-family: "sdx-icons" style:normal weight:400 stretch:100 src index:1): status=2152398850 source: http://localhost:4200/sdx-icons-p.woff2
09:38:14.019 [webpack-dev-server] Disconnected! index.js:548
09:38:14.021 [webpack-dev-server] Trying to reconnect... index.js:548
09:38:16.459 Navigated to http://localhost:8088/login
09:38:16.801 [webpack-dev-server] Live Reloading enabled. index.js:548
09:38:16.983 [DEBUG] ms-angular-auth-oidc-client-lib-id - Did not find any configured route for route http://localhost:8088/.well-known/openid-configuration angular-auth-oidc-client.mjs:160:20
09:38:16.988 [DEBUG] ms-angular-auth-oidc-client-lib-id - Did not find any configured route for route ../../assets/config/config.json angular-auth-oidc-client.mjs:160:20
09:38:17.056 Angular is running in development mode. Call enableProdMode() to enable production mode. core.mjs:24856:16
09:38:17.069 ERROR Error: Uncaught (in promise): () => new Error(`could not find matching config for state ${stateParamFromUrl}`)
Angular 19
RxJS 21
ZoneAwarePromise Angular
toPromise RxJS
getEnvironmentConfiguration config.service.ts:25
initAppConfig init.app.config.ts:6
Angular 24
7004 main.ts:14
Webpack 7
core.mjs:6484:12
21.3.22
我能够使 spring-security-oauth2-authorization-server 使用下面的配置。
- autoUserInfo 不起作用,所以我将其关闭。
- 参见 defaultSecurityFilterChain,我必须添加一个 .permitAll(),否则身份验证流的端点将受到保护。感觉有点不对。
- RegisteredClient:从示例@SteveRiesenberg mendtiond 中获取配置
@SteveRiesenberg 您认为该代码有任何改进/简化的可能吗?
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.UUID;
/**
* @author Joe Grandja
* @since 0.0.1
*/
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfigNew {
public static CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
// config.setAllowCredentials(true);
source.registerCorsConfiguration("/**", config);
return source;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.formLogin(Customizer.withDefaults()).cors().configurationSource(corsConfigurationSource()).and().build();
}
// @formatter:off
@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
RegisteredClient publicClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("medical-share-openid-connect-client-id")
.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("http://127.0.0.1:4200")
.redirectUri("http://127.0.0.1:4200/")
.redirectUri("http://127.0.0.1:4200/silent-renew.html")
.scope(OidcScopes.OPENID)
.scope("offline_access")
// .scope("message.read")
// .scope("message.write")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).requireProofKey(true).build())
.build();
// Save registered client in db as if in-memory
JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
registeredClientRepository.save(publicClient);
return registeredClientRepository;
}
// @formatter:on
@Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
}
@Bean
public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = Jwks.generateRsa();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
@Bean
public ProviderSettings providerSettings() {
return ProviderSettings.builder().issuer("http://127.0.0.1:8088").build();
// return ProviderSettings.builder().issuer("http://localhost:8088").build();
}
@Bean
public EmbeddedDatabase embeddedDatabase() {
// @formatter:off
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(EmbeddedDatabaseType.H2)
.setScriptEncoding("UTF-8")
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql")
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql")
.addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql")
.build();
// @formatter:on
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import static com.test.kubernetes.authorizationserver.application.sample.config.AuthorizationServerConfigNew.corsConfigurationSource;
import static org.springframework.security.config.Customizer.withDefaults;
@EnableWebSecurity
public class DefaultSecurityConfigNew {
// @formatter:off
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.cors().configurationSource(corsConfigurationSource()).and()
.authorizeRequests()
// .antMatchers("/.well-known/**", "/oauth2/**", "/userinfo")
.antMatchers("/**")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/login")
.authenticated()
.and()
.formLogin(withDefaults());
return http.cors().configurationSource(corsConfigurationSource()).and().build();
}
// @formatter:on
// @formatter:off
@Bean
UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("U-294b70ca67df4c018f59950f35314944")
.password("U-294b70ca67df4c018f59950f35314944")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
// @formatter:on
}
import {NgModule} from '@angular/core';
import {AuthModule, LogLevel} from 'angular-auth-oidc-client';
@NgModule({
imports: [
AuthModule.forRoot({
config: {
configId: 'ms-angular-auth-oidc-client-lib-id',
logLevel: LogLevel.Debug,
historyCleanupOff: false,
// authority: 'http://localhost:8088/realms/master',
authority: 'http://127.0.0.1:8088',
redirectUrl: 'http://127.0.0.1:4201/',
// redirectUrl: 'http://localhost:4201/',
postLogoutRedirectUri: 'http://127.0.0.1:4201/',
clientId: 'medical-share-openid-connect-client-id',
scope: 'openid offline_access',
responseType: 'code',
silentRenew: true,
renewTimeBeforeTokenExpiresInSeconds: 10,
autoUserInfo: false,
ignoreNonceAfterRefresh: true,
useRefreshToken: true,
// autoCleanStateAfterAuthentication: false,
secureRoutes: [
'http://localhost:4201/',
'localhost:4201/',
'127.0.0.1:4201/',
'http://127.0.0.1:4201/',
'http://127.0.0.1:4201',
'http://localhost:4201/',
'http://localhost:8083',
'localhost:8083',
'http://127.0.0.1:8088',
'http://127.0.0.1:8088/',
],
},
}),
],
exports: [AuthModule],
})
export class AuthConfigModule {
}
我会尽力为您面临的 questions/challenges 序列提供答案。
根据问题的评论和更新,您似乎有一些不正确的配置需要解决。来自我们的第一轮评论:
- 您似乎已经配置了机密客户端(带有客户端密码、客户端身份验证方法,
requireProofKey(true)
未设置
注意:另外,请确保您是从 http://127.0.0.1:4200
而不是 http://localhost:4200
.
浏览您的应用程序
根据您在问题中提供的更新:
- 您不想在 cors 配置中注释掉
config.setAllowCredentials(true);
,因为浏览器需要能够通过静默更新过程发送 JSESSIONID
cookie。
- Spring 授权服务器目前不支持 public 客户端的刷新令牌,因此我的示例不包含该选项
useRefreshToken: true
.
- 您的
.authorizeRequests()
DSL 用法不正确。您不希望多次调用该方法,因为第二次调用会覆盖第一次调用。您还在默认过滤器链中定位了错误的端点。它应该看起来像示例:
// @formatter:off
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated()
)
// Note: You can also make the corsConfigurationSource() non-static
// and annotated with @Bean. Then use instead:
// .cors(Customizer.withDefaults())
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.formLogin(Customizer.withDefaults());
return http.build();
}
// @formatter:on
可能还有其他问题,不好说。我建议从 this working sample (see also the angular-client auth config) 开始,并根据您正在考虑的任何自定义项对其进行改进,看看是否引入了问题。
我终于成功了:-)非常感谢@Steve。
请注意,我知道没有 RefreshToken 支持。因此,我将令牌的生命周期设为 1 天。
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.RSAKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.lang.invoke.MethodHandles;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;
@RestController
@RequestMapping()
public class OpenIdConnectMockController {
static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private KeyPair rsaKeyPair;
private RSAKey jwkRsaPublicKey;
@PostConstruct
public void generateKey() {
this.rsaKeyPair = generateRsaKey();
this.jwkRsaPublicKey = generateRsa(this.rsaKeyPair);
}
/**
* Note this method is currently not use, but here for future use.
* We use the same URL as in the Keycloak, so we have not to change things when running the thing against ms-backend-test-openid-connect-mock or Keycloak.
*/
@GetMapping(path = "/realms/master/protocol/openid-connect/certs", produces = "application/json")
public String keys() {
logger.info("Keys was called {}", this.jwkRsaPublicKey.toString());
return "{\"keys\":[" + this.jwkRsaPublicKey.toString() + "]}";
}
/**
* This is used in the SystemTest. There we download the private key and sign the JWT.
*/
@GetMapping(path = "/realms/master/protocol/openid-connect/private-key", produces = "application/json")
public byte[] getPrivateKey() throws JOSEException {
RSAKey privateKey = new RSAKey.Builder((RSAPublicKey) this.rsaKeyPair.getPublic()).privateKey(this.rsaKeyPair.getPrivate()).build();
return privateKey.toRSAPrivateKey().getEncoded();
}
public RSAKey getJwkRsaPublicKey() {
return this.jwkRsaPublicKey;
}
private RSAKey generateRsa(KeyPair keyPair) {
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
}
private KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
public class PersistedUserConfig {
@Bean
UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("U-294b70ca67df4c018f59950f35314944")
.password("U-294b70ca67df4c018f59950f35314944")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.*;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import javax.annotation.PostConstruct;
import java.lang.invoke.MethodHandles;
import java.time.Duration;
import java.util.Objects;
import java.util.UUID;
import static org.springframework.security.config.Customizer.withDefaults;
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class TestAuthorizationServerConfig {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@PostConstruct
public void publicNote() {
logger.info("****************************************************************************************************************************");
logger.info("****************************************************************************************************************************");
logger.info("Note that the current spring authorisation server does not support token renewal. If you want to test that, use the Keycloak");
logger.info("****************************************************************************************************************************");
logger.info("****************************************************************************************************************************");
}
public static CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.addAllowedOrigin("127.0.0.1:4201");
config.addAllowedOrigin("http://127.0.0.1:4201");
config.addAllowedOrigin("localhost:4201");
config.addAllowedOrigin("127.0.0.1:4200");
config.addAllowedOrigin("http://127.0.0.1:4200");
config.addAllowedOrigin("localhost:4200");
config.setAllowCredentials(true);
source.registerCorsConfiguration("/**", config);
return source;
}
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
return http.cors().configurationSource(corsConfigurationSource()).and()
.authorizeRequests()
.antMatchers("/**")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/login")
.authenticated()
.and()
.formLogin(withDefaults())
.build();
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.formLogin(withDefaults()).cors().configurationSource(corsConfigurationSource()).and().build();
}
@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
RegisteredClient publicClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("medical-share-openid-connect-client-id")
.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("http://127.0.0.1:4200")
.redirectUri("http://127.0.0.1:4200/")
.redirectUri("http://127.0.0.1:4201")
.redirectUri("http://127.0.0.1:4201/")
.scope(OidcScopes.OPENID)
.tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofDays(1)).build())
.scope("offline_access")
// .scope("message.read")
// .scope("message.write")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).requireProofKey(true).build())
.build();
// Save registered client in db as if in-memory
JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
registeredClientRepository.save(publicClient);
return registeredClientRepository;
}
@Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
}
@Bean
public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
}
@Bean
public JWKSource<SecurityContext> jwkSource(OpenIdConnectMockController openIdConnectMockController) {
RSAKey rsaKey = openIdConnectMockController.getJwkRsaPublicKey();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
@Bean
public ProviderSettings providerSettings() {
return ProviderSettings.builder().issuer("http://127.0.0.1:8088").build();
}
@Bean
public EmbeddedDatabase embeddedDatabase() {
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(EmbeddedDatabaseType.H2)
.setScriptEncoding("UTF-8")
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql")
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql")
.addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql")
.build();
}
/**
* This is a bit a hack, but as we do not know how we integrate the HealthPlattform this is a very easy way to solve the Problem for the moment.
*/
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> tokenCustomizer() {
return context -> {
Authentication principal = context.getPrincipal();
if (Objects.equals(context.getTokenType().getValue(), "access_token") && principal instanceof UsernamePasswordAuthenticationToken) {
User user = (User) principal.getPrincipal();
context.getClaims()
.claim("name", user.getUsername());
}
};
}
}
import {NgModule} from '@angular/core';
import {AuthModule, LogLevel} from 'angular-auth-oidc-client';
@NgModule({
imports: [
AuthModule.forRoot({
config: {
configId: 'ms-angular-auth-oidc-client-lib-id',
logLevel: LogLevel.Debug,
historyCleanupOff: false,
// Keycloak
// authority: 'http://localhost:8088/realms/master',
authority: 'http://127.0.0.1:8088',
redirectUrl: 'http://127.0.0.1:4201/',
// Keycloak
// redirectUrl: 'http://localhost:4201/',
postLogoutRedirectUri: 'http://127.0.0.1:4201/',
clientId: 'medical-share-openid-connect-client-id',
scope: 'openid',
responseType: 'code',
silentRenew: true,
renewTimeBeforeTokenExpiresInSeconds: 10,
autoUserInfo: false,
useRefreshToken: true,
secureRoutes: [
'http://127.0.0.1:4201',
'http://localhost:4201',
],
},
}),
],
exports: [AuthModule],
})
export class AuthConfigModule {
}
我正在使用
"angular-auth-oidc-client": "^13.1.0",
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>0.2.2</version>
</dependency>
我在我的应用程序中使用“OpenId Connect”。如果我使用 Keycloak,我的应用程序运行良好。我喜欢将其替换为 spring-security-oauth2-authorization-server。我遇到了一些我无法解决的问题。
我点击我的应用程序,然后我被重定向到登录 UI。我输入 User + PW 并表示同意。然后我再次发回我的应用程序。在这里我看到以下问题
“this.storagePersistenceService”中没有匹配的数据。
你有什么想法可能是错误的吗
此致 G
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.List;
import java.util.UUID;
/**
* @author Joe Grandja
* @since 0.0.1
*/
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfigNew {
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("*"));
configuration.setAllowedMethods(List.of("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.formLogin(Customizer.withDefaults()).cors().configurationSource(corsConfigurationSource()).and().build();
}
// @formatter:off
@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("medical-share-openid-connect-client-id")
.clientSecret("{noop}secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri("http://127.0.0.1:4200/")
.redirectUri("http://127.0.0.1:4200")
.redirectUri("http://localhost:4200/")
// .redirectUri("http://127.0.0.1:4200/login/oauth2/code/messaging-client-oidc")
// .redirectUri("http://127.0.0.1:4200/authorized")
.scope(OidcScopes.OPENID)
.scope("offline_access")
// .scope("message.read")
// .scope("message.write")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
// Save registered client in db as if in-memory
JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
registeredClientRepository.save(registeredClient);
return registeredClientRepository;
}
// @formatter:on
@Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
}
@Bean
public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = Jwks.generateRsa();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
@Bean
public ProviderSettings providerSettings() {
// return ProviderSettings.builder().issuer("http://auth-server:9000").build();
return ProviderSettings.builder().issuer("http://localhost:8088").build();
}
@Bean
public EmbeddedDatabase embeddedDatabase() {
// @formatter:off
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(EmbeddedDatabaseType.H2)
.setScriptEncoding("UTF-8")
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql")
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql")
.addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql")
.build();
// @formatter:on
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import static org.springframework.security.config.Customizer.withDefaults;
@EnableWebSecurity
public class DefaultSecurityConfigNew {
// @formatter:off
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated()
)
.formLogin(withDefaults());
return http.build();
}
// @formatter:on
// @formatter:off
@Bean
UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("U-294b70ca67df4c018f59950f35314944")
.password("U-294b70ca67df4c018f59950f35314944")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
// @formatter:on
}
/*
* Copyright 2020-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jose.jwk.ECKey;
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.nimbusds.jose.jwk.RSAKey;
import javax.crypto.SecretKey;
import java.security.KeyPair;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;
/**
* @author Joe Grandja
* @since 0.1.0
*/
public final class Jwks {
private Jwks() {
}
public static RSAKey generateRsa() {
KeyPair keyPair = KeyGeneratorUtils.generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// @formatter:off
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
// @formatter:on
}
public static ECKey generateEc() {
KeyPair keyPair = KeyGeneratorUtils.generateEcKey();
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
Curve curve = Curve.forECParameterSpec(publicKey.getParams());
// @formatter:off
return new ECKey.Builder(curve, publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
// @formatter:on
}
public static OctetSequenceKey generateSecret() {
SecretKey secretKey = KeyGeneratorUtils.generateSecretKey();
// @formatter:off
return new OctetSequenceKey.Builder(secretKey)
.keyID(UUID.randomUUID().toString())
.build();
// @formatter:on
}
}
/*
* Copyright 2020-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
/**
* @author Joe Grandja
* @since 0.1.0
*/
final class KeyGeneratorUtils {
private KeyGeneratorUtils() {
}
static SecretKey generateSecretKey() {
SecretKey hmacKey;
try {
hmacKey = KeyGenerator.getInstance("HmacSha256").generateKey();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return hmacKey;
}
static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
static KeyPair generateEcKey() {
EllipticCurve ellipticCurve = new EllipticCurve(
new ECFieldFp(
new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951")),
new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853948"),
new BigInteger("41058363725152142129326129780047268409114441015993725554835256314039467401291"));
ECPoint ecPoint = new ECPoint(
new BigInteger("48439561293906451759052585252797914202762949526041747995844080717082404635286"),
new BigInteger("36134250956749795798585127919587881956611106672985015071877198253568414405109"));
ECParameterSpec ecParameterSpec = new ECParameterSpec(
ellipticCurve,
ecPoint,
new BigInteger("115792089210356248762697446949407573529996955224135760342422259061068512044369"),
1);
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
keyPairGenerator.initialize(ecParameterSpec);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
}
import {NgModule} from '@angular/core';
import {AuthModule, LogLevel} from 'angular-auth-oidc-client';
@NgModule({
imports: [
AuthModule.forRoot({
config: {
configId: 'ms-angular-auth-oidc-client-lib-id',
logLevel: LogLevel.Debug,
historyCleanupOff: false,
authority: 'http://localhost:8088',
redirectUrl: 'http://127.0.0.1:4200' + '/',
postLogoutRedirectUri: 'http://127.0.0.1:4200' + '/',
clientId: 'medical-share-openid-connect-client-id',
scope: 'openid offline_access',
responseType: 'code',
silentRenew: true,
renewTimeBeforeTokenExpiresInSeconds: 30,
ignoreNonceAfterRefresh: true,
useRefreshToken: true,
autoUserInfo: false, // change that as it is in the example for spring security like this
secureRoutes: [
'http://localhost:4200/',
'localhost:4200/',
'127.0.0.1:4200/',
'http://127.0.0.1:4200/',
'http://localhost:4200/',
'http://localhost:8081',
'localhost:8081',
],
},
}),
],
exports: [AuthModule],
})
export class AuthConfigModule {}
09:38:13.475 Navigated to http://localhost:4200/should-login
09:38:13.682 [webpack-dev-server] Live Reloading enabled. index.js:548
09:38:13.875 [DEBUG] ms-angular-auth-oidc-client-lib-id - Did not find any configured route for route http://localhost:8088/.well-known/openid-configuration angular-auth-oidc-client.mjs:160:20
09:38:13.878 [DEBUG] ms-angular-auth-oidc-client-lib-id - Did not find any configured route for route ../../assets/config/config.json angular-auth-oidc-client.mjs:160:20
09:38:13.956 Angular is running in development mode. Call enableProdMode() to enable production mode. core.mjs:24856:16
09:38:13.967 [DEBUG] ms-angular-auth-oidc-client-lib-id - Working with config 'ms-angular-auth-oidc-client-lib-id' using http://localhost:8088 angular-auth-oidc-client.mjs:160:20
09:38:13.968 [DEBUG] ms-angular-auth-oidc-client-lib-id - currentUrl to check auth with: http://localhost:4200/should-login angular-auth-oidc-client.mjs:157:20
09:38:13.969 [DEBUG] ms-angular-auth-oidc-client-lib-id - checkAuth completed - firing events now. isAuthenticated: false angular-auth-oidc-client.mjs:160:20
09:38:13.970 Navigated to http://localhost:8088/oauth2/authorize?client_id=medical-share-openid-connect-client-id&redirect_uri=http%3A%2F%2F127.0.0.1%3A4200%2F&response_type=code&scope=openid%20offline_access&nonce=e9bd6b927b16552d4560ef708d5fd8ca49mFmzaxQ&state=771502df2eae52657faf84e1e9cf3253b3TN7VCvc&code_challenge=0AggJb7Z2ONJmNU7rAqXh9BmKf5YeEcrLlmw9lgoMJE&code_challenge_method=S256
09:38:13.970 [DEBUG] ms-angular-auth-oidc-client-lib-id - BEGIN Authorize OIDC Flow, no auth data angular-auth-oidc-client.mjs:160:20
09:38:13.971 [DEBUG] ms-angular-auth-oidc-client-lib-id - Nonce created. nonce:e9bd6b927b16552d4560ef708d5fd8ca49mFmzaxQ angular-auth-oidc-client.mjs:160:20
09:38:13.971 [DEBUG] ms-angular-auth-oidc-client-lib-id - Authorize created. adding myautostate: 771502df2eae52657faf84e1e9cf3253b3TN7VCvc angular-auth-oidc-client.mjs:160:20
09:38:13.994 downloadable font: download failed (font-family: "sdx-icons" style:normal weight:400 stretch:100 src index:1): status=2152398850 source: http://localhost:4200/sdx-icons-p.woff2
09:38:14.019 [webpack-dev-server] Disconnected! index.js:548
09:38:14.021 [webpack-dev-server] Trying to reconnect... index.js:548
09:38:16.459 Navigated to http://localhost:8088/login
09:38:16.801 [webpack-dev-server] Live Reloading enabled. index.js:548
09:38:16.983 [DEBUG] ms-angular-auth-oidc-client-lib-id - Did not find any configured route for route http://localhost:8088/.well-known/openid-configuration angular-auth-oidc-client.mjs:160:20
09:38:16.988 [DEBUG] ms-angular-auth-oidc-client-lib-id - Did not find any configured route for route ../../assets/config/config.json angular-auth-oidc-client.mjs:160:20
09:38:17.056 Angular is running in development mode. Call enableProdMode() to enable production mode. core.mjs:24856:16
09:38:17.069 ERROR Error: Uncaught (in promise): () => new Error(`could not find matching config for state ${stateParamFromUrl}`)
Angular 19
RxJS 21
ZoneAwarePromise Angular
toPromise RxJS
getEnvironmentConfiguration config.service.ts:25
initAppConfig init.app.config.ts:6
Angular 24
7004 main.ts:14
Webpack 7
core.mjs:6484:12
21.3.22 我能够使 spring-security-oauth2-authorization-server 使用下面的配置。
- autoUserInfo 不起作用,所以我将其关闭。
- 参见 defaultSecurityFilterChain,我必须添加一个 .permitAll(),否则身份验证流的端点将受到保护。感觉有点不对。
- RegisteredClient:从示例@SteveRiesenberg mendtiond 中获取配置
@SteveRiesenberg 您认为该代码有任何改进/简化的可能吗?
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.UUID;
/**
* @author Joe Grandja
* @since 0.0.1
*/
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfigNew {
public static CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
// config.setAllowCredentials(true);
source.registerCorsConfiguration("/**", config);
return source;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.formLogin(Customizer.withDefaults()).cors().configurationSource(corsConfigurationSource()).and().build();
}
// @formatter:off
@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
RegisteredClient publicClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("medical-share-openid-connect-client-id")
.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("http://127.0.0.1:4200")
.redirectUri("http://127.0.0.1:4200/")
.redirectUri("http://127.0.0.1:4200/silent-renew.html")
.scope(OidcScopes.OPENID)
.scope("offline_access")
// .scope("message.read")
// .scope("message.write")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).requireProofKey(true).build())
.build();
// Save registered client in db as if in-memory
JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
registeredClientRepository.save(publicClient);
return registeredClientRepository;
}
// @formatter:on
@Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
}
@Bean
public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = Jwks.generateRsa();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
@Bean
public ProviderSettings providerSettings() {
return ProviderSettings.builder().issuer("http://127.0.0.1:8088").build();
// return ProviderSettings.builder().issuer("http://localhost:8088").build();
}
@Bean
public EmbeddedDatabase embeddedDatabase() {
// @formatter:off
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(EmbeddedDatabaseType.H2)
.setScriptEncoding("UTF-8")
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql")
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql")
.addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql")
.build();
// @formatter:on
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import static com.test.kubernetes.authorizationserver.application.sample.config.AuthorizationServerConfigNew.corsConfigurationSource;
import static org.springframework.security.config.Customizer.withDefaults;
@EnableWebSecurity
public class DefaultSecurityConfigNew {
// @formatter:off
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.cors().configurationSource(corsConfigurationSource()).and()
.authorizeRequests()
// .antMatchers("/.well-known/**", "/oauth2/**", "/userinfo")
.antMatchers("/**")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/login")
.authenticated()
.and()
.formLogin(withDefaults());
return http.cors().configurationSource(corsConfigurationSource()).and().build();
}
// @formatter:on
// @formatter:off
@Bean
UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("U-294b70ca67df4c018f59950f35314944")
.password("U-294b70ca67df4c018f59950f35314944")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
// @formatter:on
}
import {NgModule} from '@angular/core';
import {AuthModule, LogLevel} from 'angular-auth-oidc-client';
@NgModule({
imports: [
AuthModule.forRoot({
config: {
configId: 'ms-angular-auth-oidc-client-lib-id',
logLevel: LogLevel.Debug,
historyCleanupOff: false,
// authority: 'http://localhost:8088/realms/master',
authority: 'http://127.0.0.1:8088',
redirectUrl: 'http://127.0.0.1:4201/',
// redirectUrl: 'http://localhost:4201/',
postLogoutRedirectUri: 'http://127.0.0.1:4201/',
clientId: 'medical-share-openid-connect-client-id',
scope: 'openid offline_access',
responseType: 'code',
silentRenew: true,
renewTimeBeforeTokenExpiresInSeconds: 10,
autoUserInfo: false,
ignoreNonceAfterRefresh: true,
useRefreshToken: true,
// autoCleanStateAfterAuthentication: false,
secureRoutes: [
'http://localhost:4201/',
'localhost:4201/',
'127.0.0.1:4201/',
'http://127.0.0.1:4201/',
'http://127.0.0.1:4201',
'http://localhost:4201/',
'http://localhost:8083',
'localhost:8083',
'http://127.0.0.1:8088',
'http://127.0.0.1:8088/',
],
},
}),
],
exports: [AuthModule],
})
export class AuthConfigModule {
}
我会尽力为您面临的 questions/challenges 序列提供答案。
根据问题的评论和更新,您似乎有一些不正确的配置需要解决。来自我们的第一轮评论:
- 您似乎已经配置了机密客户端(带有客户端密码、客户端身份验证方法,
requireProofKey(true)
未设置
注意:另外,请确保您是从 http://127.0.0.1:4200
而不是 http://localhost:4200
.
根据您在问题中提供的更新:
- 您不想在 cors 配置中注释掉
config.setAllowCredentials(true);
,因为浏览器需要能够通过静默更新过程发送JSESSIONID
cookie。 - Spring 授权服务器目前不支持 public 客户端的刷新令牌,因此我的示例不包含该选项
useRefreshToken: true
. - 您的
.authorizeRequests()
DSL 用法不正确。您不希望多次调用该方法,因为第二次调用会覆盖第一次调用。您还在默认过滤器链中定位了错误的端点。它应该看起来像示例:
// @formatter:off
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated()
)
// Note: You can also make the corsConfigurationSource() non-static
// and annotated with @Bean. Then use instead:
// .cors(Customizer.withDefaults())
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.formLogin(Customizer.withDefaults());
return http.build();
}
// @formatter:on
可能还有其他问题,不好说。我建议从 this working sample (see also the angular-client auth config) 开始,并根据您正在考虑的任何自定义项对其进行改进,看看是否引入了问题。
我终于成功了:-)非常感谢@Steve。
请注意,我知道没有 RefreshToken 支持。因此,我将令牌的生命周期设为 1 天。
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.RSAKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.lang.invoke.MethodHandles;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;
@RestController
@RequestMapping()
public class OpenIdConnectMockController {
static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private KeyPair rsaKeyPair;
private RSAKey jwkRsaPublicKey;
@PostConstruct
public void generateKey() {
this.rsaKeyPair = generateRsaKey();
this.jwkRsaPublicKey = generateRsa(this.rsaKeyPair);
}
/**
* Note this method is currently not use, but here for future use.
* We use the same URL as in the Keycloak, so we have not to change things when running the thing against ms-backend-test-openid-connect-mock or Keycloak.
*/
@GetMapping(path = "/realms/master/protocol/openid-connect/certs", produces = "application/json")
public String keys() {
logger.info("Keys was called {}", this.jwkRsaPublicKey.toString());
return "{\"keys\":[" + this.jwkRsaPublicKey.toString() + "]}";
}
/**
* This is used in the SystemTest. There we download the private key and sign the JWT.
*/
@GetMapping(path = "/realms/master/protocol/openid-connect/private-key", produces = "application/json")
public byte[] getPrivateKey() throws JOSEException {
RSAKey privateKey = new RSAKey.Builder((RSAPublicKey) this.rsaKeyPair.getPublic()).privateKey(this.rsaKeyPair.getPrivate()).build();
return privateKey.toRSAPrivateKey().getEncoded();
}
public RSAKey getJwkRsaPublicKey() {
return this.jwkRsaPublicKey;
}
private RSAKey generateRsa(KeyPair keyPair) {
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
}
private KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
public class PersistedUserConfig {
@Bean
UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("U-294b70ca67df4c018f59950f35314944")
.password("U-294b70ca67df4c018f59950f35314944")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.*;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import javax.annotation.PostConstruct;
import java.lang.invoke.MethodHandles;
import java.time.Duration;
import java.util.Objects;
import java.util.UUID;
import static org.springframework.security.config.Customizer.withDefaults;
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class TestAuthorizationServerConfig {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@PostConstruct
public void publicNote() {
logger.info("****************************************************************************************************************************");
logger.info("****************************************************************************************************************************");
logger.info("Note that the current spring authorisation server does not support token renewal. If you want to test that, use the Keycloak");
logger.info("****************************************************************************************************************************");
logger.info("****************************************************************************************************************************");
}
public static CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.addAllowedOrigin("127.0.0.1:4201");
config.addAllowedOrigin("http://127.0.0.1:4201");
config.addAllowedOrigin("localhost:4201");
config.addAllowedOrigin("127.0.0.1:4200");
config.addAllowedOrigin("http://127.0.0.1:4200");
config.addAllowedOrigin("localhost:4200");
config.setAllowCredentials(true);
source.registerCorsConfiguration("/**", config);
return source;
}
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
return http.cors().configurationSource(corsConfigurationSource()).and()
.authorizeRequests()
.antMatchers("/**")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/login")
.authenticated()
.and()
.formLogin(withDefaults())
.build();
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.formLogin(withDefaults()).cors().configurationSource(corsConfigurationSource()).and().build();
}
@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
RegisteredClient publicClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("medical-share-openid-connect-client-id")
.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("http://127.0.0.1:4200")
.redirectUri("http://127.0.0.1:4200/")
.redirectUri("http://127.0.0.1:4201")
.redirectUri("http://127.0.0.1:4201/")
.scope(OidcScopes.OPENID)
.tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofDays(1)).build())
.scope("offline_access")
// .scope("message.read")
// .scope("message.write")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).requireProofKey(true).build())
.build();
// Save registered client in db as if in-memory
JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
registeredClientRepository.save(publicClient);
return registeredClientRepository;
}
@Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
}
@Bean
public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
}
@Bean
public JWKSource<SecurityContext> jwkSource(OpenIdConnectMockController openIdConnectMockController) {
RSAKey rsaKey = openIdConnectMockController.getJwkRsaPublicKey();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
@Bean
public ProviderSettings providerSettings() {
return ProviderSettings.builder().issuer("http://127.0.0.1:8088").build();
}
@Bean
public EmbeddedDatabase embeddedDatabase() {
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(EmbeddedDatabaseType.H2)
.setScriptEncoding("UTF-8")
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql")
.addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql")
.addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql")
.build();
}
/**
* This is a bit a hack, but as we do not know how we integrate the HealthPlattform this is a very easy way to solve the Problem for the moment.
*/
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> tokenCustomizer() {
return context -> {
Authentication principal = context.getPrincipal();
if (Objects.equals(context.getTokenType().getValue(), "access_token") && principal instanceof UsernamePasswordAuthenticationToken) {
User user = (User) principal.getPrincipal();
context.getClaims()
.claim("name", user.getUsername());
}
};
}
}
import {NgModule} from '@angular/core';
import {AuthModule, LogLevel} from 'angular-auth-oidc-client';
@NgModule({
imports: [
AuthModule.forRoot({
config: {
configId: 'ms-angular-auth-oidc-client-lib-id',
logLevel: LogLevel.Debug,
historyCleanupOff: false,
// Keycloak
// authority: 'http://localhost:8088/realms/master',
authority: 'http://127.0.0.1:8088',
redirectUrl: 'http://127.0.0.1:4201/',
// Keycloak
// redirectUrl: 'http://localhost:4201/',
postLogoutRedirectUri: 'http://127.0.0.1:4201/',
clientId: 'medical-share-openid-connect-client-id',
scope: 'openid',
responseType: 'code',
silentRenew: true,
renewTimeBeforeTokenExpiresInSeconds: 10,
autoUserInfo: false,
useRefreshToken: true,
secureRoutes: [
'http://127.0.0.1:4201',
'http://localhost:4201',
],
},
}),
],
exports: [AuthModule],
})
export class AuthConfigModule {
}