
你好呀我的老朋友我是老寇跟我一起学习Spring gRPC集成Spring Security OAuth2引入Spring Security组件能够提供统一、可声明式的方法级安全让每个方法都可以按需进行权限控制基于Interceptor拦截器实现需要提前将Token放入Spring Authorization ServerSpring Authorization Server官方文档OAuth 2.1 授权服务器功能为OAuth 2.1 授权框架中定义的授权服务器角色提供支持。授权服务器功能提供了OAuth 2.1和OpenID Connect 1.0规范以及其他相关规范的实现。它为构建 OpenID Connect 1.0 身份提供程序和 OAuth 2.1 授权服务器产品提供了一个安全、轻量级且可定制的基础。client_credentials实现client_credentials认证模式的OAuth2认证服务增加依赖dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-webmvc/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security-oauth2-authorization-server/artifactId/dependency/dependencies具体代码SecurityConfigConfigurationpublicclassSecurityConfig{BeanRegisteredClientRepositoryregisteredClientRepository(PasswordEncoderpasswordEncoder,Value(${app.oauth2.client.id})StringclientId,Value(${app.oauth2.client.secret})StringclientSecret){RegisteredClientclientRegisteredClient.withId(UUID.randomUUID().toString()).clientId(clientId).clientSecret(passwordEncoder.encode(clientSecret)).clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC).clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST).authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).scope(read).tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofHours(1)).build()).build();returnnewInMemoryRegisteredClientRepository(client);}BeanPasswordEncoderpasswordEncoder(){returnPasswordEncoderFactories.createDelegatingPasswordEncoder();}BeanJWKSourceSecurityContextjwkSource(){RSAKeyrsaKeygenerateRsa();JWKSetjwkSetnewJWKSet(rsaKey);return(jwkSelector,_)-jwkSelector.select(jwkSet);}BeanJwtDecoderjwtDecoder(JWKSourceSecurityContextjwkSource){returnOAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);}BeanAuthorizationServerSettingsauthorizationServerSettings(){returnAuthorizationServerSettings.builder().build();}BeanOrder(1)SecurityFilterChainauthorizationServerSecurityFilterChain(HttpSecurityhttp){OAuth2AuthorizationServerConfigurerauthorizationServerConfigurernewOAuth2AuthorizationServerConfigurer();RequestMatcherendpointsMatcherauthorizationServerConfigurer.getEndpointsMatcher();http.securityMatcher(endpointsMatcher);http.with(authorizationServerConfigurer,Customizer.withDefaults());http.authorizeHttpRequests(authorize-authorize.anyRequest().authenticated());returnhttp.build();}privatestaticRSAKeygenerateRsa(){KeyPairkeyPairgenerateRsaKey();RSAPublicKeypublicKey(RSAPublicKey)keyPair.getPublic();RSAPrivateKeyprivateKey(RSAPrivateKey)keyPair.getPrivate();returnnewRSAKey.Builder(publicKey).privateKey(privateKey).keyUse(KeyUse.SIGNATURE).keyID(UUID.randomUUID().toString()).build();}privatestaticKeyPairgenerateRsaKey(){try{KeyPairGeneratorkeyPairGeneratorKeyPairGenerator.getInstance(RSA);keyPairGenerator.initialize(2048);returnkeyPairGenerator.generateKeyPair();}catch(NoSuchAlgorithmExceptionex){thrownewIllegalStateException(RSA algorithm is not available,ex);}}}OAuth2TestAppSpringBootApplication public class OAuth2TestApp { public static void main(String[] args) { SpringApplication.run(OAuth2TestApp.class, args); } }application.ymlserver:port:9088app:oauth2:client:id:client-idsecret:client-secretlogging:level:org.springframework.security:info测试curl-umachine-client:machine-secret\-HContent-Type: application/x-www-form-urlencoded\-dgrant_typeclient_credentialsscopemessage.read\http://localhost:9088/oauth2/tokenSpring gRPC Server集成Spring SecuritySpring gRPC Server Security文档地址采用全局拦截GlobalServerInterceptorSpring OAuth2 Resource Server拦截Token并请求Spring Authorization Server的JWK Set Endpoint/oauth2/jwks引入依赖dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-grpc-server/artifactId /dependency dependency groupIdorg.springframework.grpc/groupId artifactIdspring-grpc-core/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-oauth2-resource-server/artifactId /dependency /dependencies具体代码/** * * a href * https://docs.springframework.org.cn/spring-security/reference/reactive/oauth2/resource-server/opaque-token.htmlOAuth * 2.0 资源服务器不透明令牌/a. * p * a hrefhttps://docs.spring.io/spring-grpc/reference/server.htmlSpring gRPC 安全/a * p * a href * https://github.com/spring-projects/spring-grpc/tree/main/samples/grpc-oauth2Spring * gRPC 安全例子/a * * author laokou */ConfigurationEnableMethodSecurityclassGrpcOAuth2Config{BeanGlobalServerInterceptorAuthenticationProcessInterceptorjwtAuthenticationProcessInterceptor(GrpcSecuritygrpc)throwsException{returngrpc.authorizeRequests(requests-requests.allRequests().authenticated()).oauth2ResourceServer((resourceServer)-resourceServer.jwt(Customizer.withDefaults())).build();}}appcation.ymlspring:security:oauth2:resourceserver:jwt:jwk-set-uri:http://127.0.0.1:9088/oauth2/jwks方法拦截示例Slf4jGrpcServiceRequiredArgsConstructorpublicclassxxxImplextendsxxxGrpc.xxxImplBase{privatefinalStringresultMsgMessageUtils.getMessage(StatusCode.OK);OverridePreAuthorize(hasAuthority(SCOPE_read))publicvoidgenerateId(GenerateIdRequestrequest,StreamObserverxxxResponseresponseObserver){GenerateIdResponseresponseGenerateIdResponse.newBuilder().setCode(StatusCode.OK).setMsg(resultMsg).setData(1).build();responseObserver.onNext(response);responseObserver.onCompleted();}}Spring gRPC Client集成Spring SecuritySpring gRPC Client Security文档地址采用全局拦截GlobalClientInterceptor从Spring Authorization Server获取Token并塞入Interceptor依赖dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-grpc-client/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-oauth2-client/artifactId/dependency具体代码ConfigurationfinalclassGrpcClientConfig{BeanOAuth2AuthorizedClientManagerauthorizedClientManager(ClientRegistrationRepositoryregistrations,OAuth2AuthorizedClientServiceservice){OAuth2AuthorizedClientProviderproviderOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials().build();AuthorizedClientServiceOAuth2AuthorizedClientManagermanagernewAuthorizedClientServiceOAuth2AuthorizedClientManager(registrations,service);manager.setAuthorizedClientProvider(provider);returnmanager;}BeanGlobalClientInterceptorClientInterceptorclientInterceptor(OAuth2AuthorizedClientManagerauthorizedClientManager){returnnewBearerTokenAuthenticationInterceptor(()-{// 塞入tokendefault来源于yaml配置请查看yaml配置OAuth2AuthorizeRequestrequestOAuth2AuthorizeRequest.withClientRegistrationId(default).principal(system).build();OAuth2AuthorizedClientclientauthorizedClientManager.authorize(request);Assert.notNull(client,authorized client is null);returnclient.getAccessToken().getTokenValue();});}}application.ymlspring:security:oauth2:client:registration:default:client-id:client-idclient-secret:client-secretclient-name:OAuth2.1 M2M认证客户端authorization-grant-type:client_credentialsclient-authentication-method:client_secret_basicscope:-readprovider:localprovider:local:token-uri:http://127.0.0.1:9088/oauth2/token我是老寇我们下次再见啦