spring authorization server 0.3.1 - 默认oidc
- 开始
- 1、default-authorizationserver项目
- 1.1、AuthorizationServerConfig.java
- 1.2、DefaultSecurityConfig.java
- 1.3、Jwks.java
- 1.4、KeyGeneratorUtils.java
- 1.5、DefaultAuthorizationServer.java
- 1.6、application.yml
- 2、client项目
- 2.1、SecurityConfig.java
- 2.2、WebClientConfig.java
- 2.3、AuthorizationController.java
- 2.4、DefaultController.java
- 2.5、Client1Application.java
- 2.6、templates/index.html
- 2.7、application.yml
- 3、resource项目
- 3.1、ResourceServerConfig.java
- 3.2、MessagesController.java
- 3.3、MessageResourceApplication.java
- 3.4、application.yml
开始
spring security oauth已停更 spring security oauth migration guide ,新授权项目已迁移至spring authorization server,spring authorization server发展不容易,终于到了稍微稳当的版本。本文主要以源代码当中的示例为主,因源代码版本之间差异较大,部分示例代码会稍微改动。
演示代码请移步
spring authorization server default 示例代码
1、default-authorizationserver项目
1.1、AuthorizationServerConfig.java
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0// @formatter:offhttp.exceptionHandling(exceptions ->exceptions.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))).oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);// @formatter:onreturn http.build();}// @formatter:off@Beanpublic RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString()).clientId("messaging-client").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:8080/login/oauth2/code/messaging-client-oidc").redirectUri("http://127.0.0.1:8080/authorized").scope(OidcScopes.OPENID).scope(OidcScopes.PROFILE).scope("message.read").scope("message.write").clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();// Save registered client in db as if in-memoryJdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);registeredClientRepository.save(registeredClient);return registeredClientRepository;}// @formatter:on@Beanpublic OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);}@Beanpublic OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);}@Beanpublic JWKSource<SecurityContext> jwkSource() {RSAKey rsaKey = Jwks.generateRsa();JWKSet jwkSet = new JWKSet(rsaKey);return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);}@Beanpublic JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);}@Beanpublic ProviderSettings authorizationServerSettings() {return ProviderSettings.builder().issuer("http://localhost:9000").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
// }}
1.2、DefaultSecurityConfig.java
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class DefaultSecurityConfig {// @formatter:off@BeanSecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize ->authorize.anyRequest().authenticated()).formLogin(withDefaults());return http.build();}// @formatter:on// @formatter:off@BeanUserDetailsService users() {UserDetails user = User.withDefaultPasswordEncoder().username("user1").password("password").roles("USER").build();return new InMemoryUserDetailsManager(user);}// @formatter:on}
1.3、Jwks.java
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:offreturn 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:offreturn new ECKey.Builder(curve, publicKey).privateKey(privateKey).keyID(UUID.randomUUID().toString()).build();// @formatter:on}public static OctetSequenceKey generateSecret() {SecretKey secretKey = KeyGeneratorUtils.generateSecretKey();// @formatter:offreturn new OctetSequenceKey.Builder(secretKey).keyID(UUID.randomUUID().toString()).build();// @formatter:on}
}
1.4、KeyGeneratorUtils.java
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;}
}
1.5、DefaultAuthorizationServer.java
@SpringBootApplication
public class DefaultAuthorizationServer {public static void main(String[] args) {SpringApplication.run(DefaultAuthorizationServer.class, args);}}
1.6、application.yml
server:port: 9000spring:devtools:restart:enabled: truelivereload:enabled: truedatasource:driver-class-name: com.mysql.cj.jdbc.Driverusername: 数据库用户名password: 数据库密码url: jdbc:mysql://localhost:3306/数据库名称?serverTimezone=GMT%2B8&&useSSL=falselogging:level:root: INFOorg.springframework.web: INFOorg.springframework.security: INFOorg.springframework.security.oauth2: INFO
# org.springframework.boot.autoconfigure: DEBUG
2、client项目
2.1、SecurityConfig.java
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class SecurityConfig {@BeanWebSecurityCustomizer webSecurityCustomizer() {return (web) -> web.ignoring().requestMatchers("/webjars/**");}// @formatter:off@BeanSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize ->authorize.anyRequest().authenticated()).oauth2Login(oauth2Login ->oauth2Login.loginPage("/oauth2/authorization/messaging-client-oidc").defaultSuccessUrl("/", true)).oauth2Client(withDefaults());return http.build();}// @formatter:on}
2.2、WebClientConfig.java
@Configuration
public class WebClientConfig {@BeanWebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);return WebClient.builder().apply(oauth2Client.oauth2Configuration()).build();}@BeanOAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clientRegistrationRepository,OAuth2AuthorizedClientRepository authorizedClientRepository) {OAuth2AuthorizedClientProvider authorizedClientProvider =OAuth2AuthorizedClientProviderBuilder.builder().authorizationCode().refreshToken().clientCredentials().build();DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository);authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);return authorizedClientManager;}
}
2.3、AuthorizationController.java
@Controller
public class AuthorizationController {private final WebClient webClient;private final String messagesBaseUri;public AuthorizationController(WebClient webClient,@Value("${messages.base-uri}") String messagesBaseUri) {this.webClient = webClient;this.messagesBaseUri = messagesBaseUri;}@GetMapping(value = "/authorize", params = "grant_type=authorization_code")public String authorizationCodeGrant(Model model,@RegisteredOAuth2AuthorizedClient("messaging-client-authorization-code")OAuth2AuthorizedClient authorizedClient) {String[] messages = this.webClient.get().uri(this.messagesBaseUri).attributes(oauth2AuthorizedClient(authorizedClient)).retrieve().bodyToMono(String[].class).block();model.addAttribute("messages", messages);return "index";}// '/authorized' is the registered 'redirect_uri' for authorization_code@GetMapping(value = "/authorized", params = OAuth2ParameterNames.ERROR)public String authorizationFailed(Model model, HttpServletRequest request) {String errorCode = request.getParameter(OAuth2ParameterNames.ERROR);if (StringUtils.hasText(errorCode)) {model.addAttribute("error",new OAuth2Error(errorCode,request.getParameter(OAuth2ParameterNames.ERROR_DESCRIPTION),request.getParameter(OAuth2ParameterNames.ERROR_URI)));}return "index";}@GetMapping(value = "/authorize", params = "grant_type=client_credentials")public String clientCredentialsGrant(Model model) {String[] messages = this.webClient.get().uri(this.messagesBaseUri).attributes(clientRegistrationId("messaging-client-client-credentials")).retrieve().bodyToMono(String[].class).block();model.addAttribute("messages", messages);return "index";}
}
2.4、DefaultController.java
@Controller
public class DefaultController {@GetMapping("/")public String root() {return "redirect:/index";}@GetMapping("/index")public String index() {return "index";}@ResponseBody@GetMapping("/code")public String code(String code) {return code;}
}
2.5、Client1Application.java
@SpringBootApplication
public class Client1Application {public static void main(String[] args) {SpringApplication.run(Client1Application.class, args);}}
2.6、templates/index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"><head><title>Spring Security OAuth 2.0 Sample</title><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" /></head><body><div th:fragment="header"><nav class="navbar navbar-default"><div class="container"><div class="container-fluid"><div class="navbar-collapse collapse" id="navbar"></div></div></div></nav></div><div class="container"><div th:if="${error}" class="alert alert-danger alert-dismissible" role="alert"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button><h4 th:text="${error}" class="text-center"></h4></div><div class="panel panel-default"><div class="panel-heading"><h3 class="panel-title">Authorize the client using <span style="font-family:monospace">grant_type</span>:</h3></div><ul class="list-group"><li class="list-group-item"><a href="/authorize?grant_type=authorization_code" th:href="@{/authorize?grant_type=authorization_code}"><span style="font-size:medium">Authorization Code</span> <small class="text-muted">(Login to Spring Authorization Server using: user1/password)</small></a></li><li class="list-group-item"><a href="/authorize?grant_type=client_credentials" th:href="@{/authorize?grant_type=client_credentials}"><span style="font-size:medium">Client Credentials</span></a></li></ul><div th:if="${messages}" class="panel-footer"><h4>Messages:</h4><table class="table table-condensed"><tbody><tr class="row" th:each="message : ${messages}"><td th:text="${message}">message</td></tr></tbody></table></div></div></div><script src="/webjars/jquery/jquery.min.js" th:src="@{/webjars/jquery/jquery.min.js}"></script><script src="/webjars/bootstrap/js/bootstrap.min.js" th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script></body>
</html>
2.7、application.yml
server:port: 8080logging:level:root: INFOorg.springframework.web: INFOorg.springframework.security: INFOorg.springframework.security.oauth2: INFO
# org.springframework.boot.autoconfigure: DEBUGspring:thymeleaf:cache: falsesecurity:oauth2:client:registration:messaging-client-oidc:provider: springclient-id: messaging-clientclient-secret: secretauthorization-grant-type: authorization_coderedirect-uri: "http://127.0.0.1:8080/login/oauth2/code/{registrationId}"scope: openid, profileclient-name: messaging-client-oidcmessaging-client-authorization-code:provider: springclient-id: messaging-clientclient-secret: secretauthorization-grant-type: authorization_coderedirect-uri: "http://127.0.0.1:8080/authorized"scope: message.read,message.writeclient-name: messaging-client-authorization-codemessaging-client-client-credentials:provider: springclient-id: messaging-clientclient-secret: secretauthorization-grant-type: client_credentialsscope: message.read,message.writeclient-name: messaging-client-client-credentialsprovider:spring:issuer-uri: http://localhost:9000messages:base-uri: http://127.0.0.1:8090/messages
3、resource项目
3.1、ResourceServerConfig.java
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class ResourceServerConfig {// @formatter:off@BeanSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.csrf().disable().cors().disable().securityMatcher("/messages/**", "/user/**").authorizeHttpRequests().anyRequest().authenticated().and()
// .authorizeHttpRequests()
// .requestMatchers("/messages/**").hasAuthority("SCOPE_message.read")
// .and().oauth2ResourceServer().jwt();return http.build();}// @formatter:on}
3.2、MessagesController.java
@RestController
public class MessagesController {@GetMapping("/messages")public String[] getMessages() {return new String[] {"Message 1", "Message 2", "Message 3"};}@GetMapping("/user")public Jwt user() {Jwt principal = (Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal();System.out.println(principal);// 查询数据库获取用户信息return principal;}
}
3.3、MessageResourceApplication.java
@SpringBootApplication
public class MessagesResourceApplication {public static void main(String[] args) {SpringApplication.run(MessagesResourceApplication.class, args);}}
3.4、application.yml
server:port: 8090logging:level:root: INFOorg.springframework.web: INFOorg.springframework.security: INFOorg.springframework.security.oauth2: INFO
# org.springframework.boot.autoconfigure: DEBUGspring:security:oauth2:resourceserver:jwt:issuer-uri: http://localhost:9000