springcloud gateway作为oauth2客户端
springcloud gateway作为oauth2客户端gateway配置application.yml其网关的配置需要加上过滤器(加上过滤器后会走TokenRelayGatewayFilterFactory过滤器,进行token验证)如使用默认自动方式去配置授权路径等会被拦截,因此我们自定义SecurityConfig授权服务器读取证书并配置JWT的JwtAccessTokenConvert
springcloud gateway作为oauth2客户端
通过对springcloud gateway以及oauth2的进一步了解这里的配置其实是将gateway最为了oauth2的资源服务器使用,适用场景:只需要网关做一些token验证的工作,具体的授权步骤由其他服务完成(前台或者其他后台服务),如果希望网关能够做一些授权步骤并且可以管理token的可以转战我的另一篇文章springcloud gateway作为oauth2的客户端进化篇
gateway配置application.yml
spring:
# 加上此配置可自动配置安全验证及JWT验证
autoconfigure:
exclude: org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagementWebSecurityAutoConfiguration
security:
oauth2:
# 自定义配置安全验证JWT验证时需要配置resourceserver.jwt.jwk-set-uri
resourceserver:
jwt.jwk-set-uri: http://zw-pc3-pc:8080/uaa/oauth/jwks
client:
registration:
login-client:
provider: uaa
client-id: android
client-secret: android
authorization-grant-type: authorization_code
redirect-uri-template: "{baseUrl}/login/oauth2/code/{registrationId}"
scope: xx
provider:
uaa:
authorization-uri: http://zw-pc3-pc:8080/uaa/oauth/authorize
token-uri: http://zw-pc3-pc:8080/uaa/oauth/token
user-info-uri: http://zw-pc3-pc:8080/uaa/a/user/user
user-name-attribute: sub
jwk-set-uri: http://zw-pc3-pc:8080/uaa/oauth/jwks
其网关的配置需要加上过滤器(加上过滤器后会走TokenRelayGatewayFilterFactory过滤器,进行token验证)
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
defaultFilters:
- PreserveHostHeader
routes:
# 消费者
- id: EUREKA-CONSUMER
uri: lb://EUREKA-CONSUMER
predicates:
- Path=/consumer/**
filters:
- StripPrefix=1
- TokenRelay=
如使用默认自动方式去配置授权路径等会被拦截,因此我们自定义SecurityConfig
package com.example.config.oauth;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.server.resource.web.server.ServerBearerTokenAuthenticationConverter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.URI;
/**
* 安全配置
* @ EnableWebSecurity 启用web安全配置
*/
@Configuration
public class SecurityConfig{
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
ServerBearerTokenAuthenticationConverter bearerTokenConverter = new ServerBearerTokenAuthenticationConverter();
http
.authorizeExchange().pathMatchers("/uaa/oauth/**","/uaa/login","/oauth/**").permitAll()
.anyExchange().authenticated()
.and()
.formLogin().loginPage("/uaa/oauth/login")
// 禁用csrf
.and().csrf().disable();
http.oauth2ResourceServer().jwt();
return http.build();
}
/**
* 定义OAuth2请求匹配器
*/
private static class OAuth2RequestedMatcher implements ServerWebExchangeMatcher {
@Override
public Mono<MatchResult> matches(ServerWebExchange serverWebExchange) {
URI uri = serverWebExchange.getRequest().getURI();
String path = uri.getPath();
if(!path.contains("/oauth||/uaa/oauth||uaa/login")){
return OAuth2RequestedMatcher.MatchResult.match();
}
return OAuth2RequestedMatcher.MatchResult.notMatch();
}
}
}
【注】:
1、http.oauth2ResourceServer().jwt();一行加了JWT验证需要的过滤器
2、如果没有配置authenticationManager,则会使用默认的JwtReactiveAuthenticationManager进行token验证,authenticationManager中使用的ReactiveJwtDecoder是NimbusReactiveJwtDecoder,NimbusReactiveJwtDecoder默认使用RS256(RSA)加密算法,因之前授权服务器使用的JWT加密算法为HS256(HS256),两者不匹配,因此为了适应gateway,将授权服务器的加密算法也换成了RSA,使用RSA需要生成证书(可使用jdk的keytool生成),授权服务器需提供其证书keys,如:
{
“keys”: [{
“alg”: “RS256”,
“kty”: “RSA”,
“use”: “sig”,
“n”: “AKh0Ckt9phaUql8w1WeqzGiMec1S8aGjwFBBtdinJiGs7Vp2sI7mNtg4RQesHhz7AKAdBKh3JyP6OwgzuDWAtcKA8DZx58GfBUe7lkFwF0OizIqoT/wvnoAeII27G/18vYSakXalz58O2+9d0dWFag4jH8yG0V2v1JrM+GmFxx3JK9o8Im4wvI8ssikV9ZgqT60CDeSCV/3ayICiIyBVxZ11TsxUKdKcmaqTjqjZJZQhRq0uU/Y2Z2Zvq5a7OjNOqyOaDmquxzVeikEK036VErbuc2vjEXA4Vbzoxrn8xSI1rB8p+Wu9qbArk1MQOnPgaruNyA7uKwYL4/nvBl7UDWs=”,
“e”: “AQAB”,
“kid”: “Y2VzaGk=”,
“x5t”: “MIIDpTCCAo2gAwIBAgIEcW54sjANBgkqhkiG9w0BAQsFADCBgjEPMA0GA1UEBgwG5Lit5Zu9MQ8wDQYDVQQIDAbmsrPljZcxDzANBgNVBAcMBumDkeW3njEQMA4GA1UEChMHVW5rbm93bjEtMCsGA1UECwwk5rKz5Y2X5Lit57u055S15a2Q6IKh5Lu95pyJ6ZmQ5YWs5Y+4MQwwCgYDVQQDDAPliJgwHhcNMTkxMTEzMDY1NzEyWhcNMjAxMjE3MDY1NzEyWjCBgjEPMA0GA1UEBgwG5Lit5Zu9MQ8wDQYDVQQIDAbmsrPljZcxDzANBgNVBAcMBumDkeW3njEQMA4GA1UEChMHVW5rbm93bjEtMCsGA1UECwwk5rKz5Y2X5Lit57u055S15a2Q6IKh5Lu95pyJ6ZmQ5YWs5Y+4MQwwCgYDVQQDDAPliJgwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCodApLfaYWlKpfMNVnqsxojHnNUvGho8BQQbXYpyYhrO1adrCO5jbYOEUHrB4c+wCgHQSodycj+jsIM7g1gLXCgPA2cefBnwVHu5ZBcBdDosyKqE/8L56AHiCNuxv9fL2EmpF2pc+fDtvvXdHVhWoOIx/MhtFdr9SazPhphccdySvaPCJuMLyPLLIpFfWYKk+tAg3kglf92siAoiMgVcWddU7MVCnSnJmqk46o2SWUIUatLlP2Nmdmb6uWuzozTqsjmg5qrsc1XopBCtN+lRK27nNr4xFwOFW86Ma5/MUiNawfKflrvamwK5NTEDpz4Gq7jcgO7isGC+P57wZe1A1rAgMBAAGjITAfMB0GA1UdDgQWBBRjfM4BsO63e7UEkI8t3usv2WQ+cDANBgkqhkiG9w0BAQsFAAOCAQEAUGmcVwYRO3gCTOmwL+xu86+lY7xQp5q7nxS9fvOFz1i3Sy2fbg1crKUQ+TL57syDUEMqrnyn928aOv5yWCj4Fejag5rxc5PCzSuZyY3Ac7Up00H41+TcJNq2E/ZUBjEvq85sEPQFxZAsSxg12B4SwOxKIvlTtjUrfCywKSh8hI9IRYrpfxU0lg7CnCoNCFMY811z2HiqgMR7FJmupjpl62ESI+76ut9diCm9gv5kR10qoPpLwSxIVbbMoW4K9TI8AWDQ2fK0V2kPRLjj1ae1w9hKr5I4SN5LDOBl6zMjlaAllnSmW73aBsXyB53+94ws3Bsi2RqMcVpsuSPX29q/Yg==”
}]
}
授权服务器读取证书并配置JWT的JwtAccessTokenConverter
/**
* token生成处理:指定签名
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
try {
KeyStore keyStore = KeyStore.getInstance("JKS");
FileInputStream in = new FileInputStream("D:\\keystores\\mykeystore");
keyStore.load(in,"123456".toCharArray());
Enumeration aliasEnum = keyStore.aliases();
String keyAlias = "" ;
while (aliasEnum.hasMoreElements()) {
keyAlias = (String) aliasEnum.nextElement();
}
PublicKey publicKey = keyStore.getCertificate(keyAlias).getPublicKey();
//加载私钥,这里填私钥密码
PrivateKey privateKey = ((KeyStore.PrivateKeyEntry) keyStore.getEntry(keyAlias,
new KeyStore.PasswordProtection("123456".toCharArray()))).getPrivateKey();
String _privateKey = Base64.encodeBase64String(privateKey.getEncoded());
KeyPair keyPair = new KeyPair(publicKey,privateKey);
accessTokenConverter.setKeyPair(keyPair);
}catch (Exception e){
throw new IllegalStateException("Cannot load keys from store: " , e);
}
return accessTokenConverter;
}
授权服务器提供jwks.json接口( http://zw-pc3-pc:8080/uaa/oauth/jwks),其中:“n”:公钥模数,“e”:公钥指数,"kid"以及x5t可通过以下代码获取
@Test
public void test() throws Exception {
KeyStore keyStore = KeyStore.getInstance("JKS");
FileInputStream in = new FileInputStream("D:\\keystores\\mykeystore");
keyStore.load(in,"123456".toCharArray());
Enumeration aliasEnum = keyStore.aliases();
String keyAlias = "" ;
while (aliasEnum.hasMoreElements()) {
keyAlias = (String) aliasEnum.nextElement();
}
System.out.println("别名"+keyAlias);
Certificate cert = keyStore.getCertificate(keyAlias);
RSAPublicKey publicKey = (RSAPublicKey)cert.getPublicKey();
String publick = Base64.encodeBase64String(publicKey.getEncoded());
BigInteger publicModulus = publicKey.getModulus();
BigInteger publicExponent = publicKey.getPublicExponent();
System.out.println("publicKey:"+publick);
System.out.println("publicModulus:"+publicModulus.toString());
System.out.println("publicExponent:"+publicExponent.toString());
//加载私钥,这里填私钥密码
RSAPrivateKey privateKey = (RSAPrivateKey)((KeyStore.PrivateKeyEntry) keyStore.getEntry(keyAlias,
new KeyStore.PasswordProtection("123456".toCharArray()))).getPrivateKey();
String _privateKey = Base64.encodeBase64String(privateKey.getEncoded());
System.out.println("privateKey:"+_privateKey);
System.out.println("privateModulus:"+privateKey.getModulus());
System.out.println("n:"+Base64.encodeBase64String(publicModulus.toByteArray()));
System.out.println("e:"+Base64.encodeBase64String(publicExponent.toByteArray()));
System.out.println("x5t:"+Base64.encodeBase64String(cert.getEncoded()));
System.out.println("kid:"+Base64.encodeBase64String(keyAlias.getBytes()));
}
gateway还可以通过其他方式实现oauth客户端
以上使用的是在本地通过授权服务器的/oauth/jwks接口获取公钥在网关本地进行token验证,还可以通过授权服务器的oauth/check_token接口进行token验证,这就需要自定义authenticationManager,暂未实现后面这种方法。
更多推荐
所有评论(0)