Spring Cloud 之 Gateway、Spring Security、Oauth2 的整合

源码地址:看这里,看这里,看这里

gateway为微服务的网关,所有的访问服务的api都要通过网关转发到内网对应的服务。spring security、oauth2为认证和授权中心,负责整个微服务api的安全

版本说明:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
    </parent>
	<dependency>
	    <groupId>org.springframework.cloud</groupId>
	    <artifactId>spring-cloud-dependencies</artifactId>
	    <version>Hoxton.SR8</version>
	    <type>pom</type>
	    <scope>import</scope>
	</dependency>

1)learn-shop-public-auth(认证中心)

认证中心,主要是用于用户的登陆认证,发放token

1、生成JKS

使用jdk的keytool生成jks,记住生成的时填入的密码

keytool -genkeypair

将生成的 jwt.jks 文件放到 resources 目录下

2、添加pom.xml中主要依赖

   	<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
   	<dependency>
        <groupId>com.nimbusds</groupId>
        <artifactId>nimbus-jose-jwt</artifactId>
        <version>8.16</version>
    </dependency>

3、添加主要类

3.1 目录结构及主要类说明

在这里插入图片描述

3.2 WebSecurityConfig

下面展示一些 内联代码片

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthProperties authProperties;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
                // 配置的白名单
                .antMatchers(ArrayUtil.toArray(authProperties.getWhiteList(), String.class)).permitAll()
                .anyRequest().authenticated();


    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        // 密码加密
        return new BCryptPasswordEncoder();
    }
}

3.3 Oauth2ServerConfig 配置

@Configuration
@EnableAuthorizationServer
public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private DataSource dataSource;
    @Autowired
    private AuthProperties authProperties;
    @Autowired
    private UserDetailsService customUserDetailsService;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 客户端访问方式配置数据在数据库中
        clients.withClientDetails(customClientDetailsService());
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer);
        delegates.add(accessTokenConverter());
        enhancerChain.setTokenEnhancers(delegates); //配置JWT的内容增强器
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(customUserDetailsService) //配置加载用户信息的服务
                .accessTokenConverter(accessTokenConverter())
                .tokenEnhancer(enhancerChain);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients();
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setKeyPair(keyPair());
        return jwtAccessTokenConverter;
    }

    @Bean
    public KeyPair keyPair() {
        TokenProperties token = authProperties.getToken();
        String jwtFileName = token.getJwtFileName();
        String jwtPassword = token.getJwtPassword();
        String alias = token.getAlias();
        //从classpath下的证书中获取秘钥对
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource(jwtFileName), jwtPassword.toCharArray());
        return keyStoreKeyFactory.getKeyPair(alias, jwtPassword.toCharArray());
    }

    @Bean
    public ClientDetailsService customClientDetailsService() {
        return new JdbcClientDetailsService(dataSource);
    }
}

3.4 JwtTokenEnhancer

@Component
public class JwtTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();
        Map<String, Object> info = new HashMap<>();
        //把用户ID设置到JWT中
        info.put("id", securityUser.getId());
        info.put("usercode",securityUser.getUsercode());
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
        return accessToken;
    }
}

3.5 CustomUserDetailsService

public class CustomUserDetailsService implements UserDetailsService {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 查询用户的权限(我是写死的)
        Set<GrantedAuthority> userAuthotities = new HashSet<>();
        userAuthotities.add(new SimpleGrantedAuthority("query-demo"));
        UserPo userPo = new UserPo("admin",passwordEncoder.encode("admin"));
        // 注意如果在WebSecurityConfig.java 中使用的密文时,这个地方也要使用密文
        return new SecurityUser(userPo , userAuthotities);
    }
}

4、添加公钥访问地址

@RestController
public class KeyPairController {

    @Autowired
    private KeyPair keyPair;

    /**
     * 获取RSA公钥
     *
     * @return
     */
    @GetMapping("/rsa/publicKey")
    public Map<String, Object> getKey() {
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAKey key = new RSAKey.Builder(publicKey).build();
        return new JWKSet(key).toJSONObject();
    }
}

5、添加配置文件

auth:
  token:
    alias: jwt
    jwtFileName: jwt.jks
    jwtPassword: 123456789
  white-list:
    - "/rsa/publicKey" # 获取公钥

2)learn-cloud-gateway(资源中心)

资源中心主要于鉴定用户是否有权限访问该资源

1、目录结构

在这里插入图片描述

2、AuthorizationManager

@Slf4j
@Component
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    private final static String PERMISSION = RedisCst.ROLE_PERMISSION_KEY;
    private final static String DICTIONARY = RedisCst.COMM_DICTIONARY_SYS_MODULE;

    @Autowired
    private RedisUtils redisUtils;

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
        //从Redis中获取当前路径可访问角色列表
        URI uri = authorizationContext.getExchange().getRequest().getURI();
        String targetURI = uri.getPath();
        log.info("<<<=== targetURI:{}", targetURI);
        // 获取 redis 中数据字典的数据
        Map<String, String> dictionaryMap = redisUtils.getArray(DICTIONARY + DictionaryType.SYS_MC_SYSTEM_MODULE, DataDictionaryVo.class)
                .stream()
                .filter(f -> DictionaryType.SYS_FC_SYSTEM_MODULE.equals(f.getFieldType()))
                .collect(Collectors.toMap(DataDictionaryVo::getFieldValue, DataDictionaryVo::getFieldDisplay));

        //认证通过且角色匹配的用户可访问当前路径
        return mono
                .filter(Authentication::isAuthenticated)
                .flatMapIterable(Authentication::getAuthorities)
                .map(GrantedAuthority::getAuthority)
                .any(role -> {
                    role = role.replace(AuthConstant.AUTHORITY_PREFIX, "");
                    List<PermissionVo> permissionVos = redisUtils.getArray(PERMISSION + role, PermissionVo.class);
                    if (permissionVos == null) {
                        return false;
                    }
                    for (PermissionVo permissionVo : permissionVos) {
                        if (ToolsUtils.isEmpty(permissionVo.getSystemModule())) {
                            String sourceURI = permissionVo.getUrl();
                            log.info("===>>> sourceURI:{}", sourceURI);
                            if (antPathMatcher.match(sourceURI, targetURI)) {
                                log.info("\n===>>> sourceURI:{},targetURI:{} <<<===\n", sourceURI, targetURI);
                                return true;
                            }
                        } else {
                            String[] split = permissionVo.getSystemModule().split(",");
                            for (String s : split) {
                                String sourceURI = "/" + dictionaryMap.get(s) + permissionVo.getUrl();
                                log.info("===>>> sourceURI:{}", sourceURI);
                                if (antPathMatcher.match(sourceURI, targetURI)) {
                                    log.info("\n===>>> sourceURI:{},targetURI:{} <<<===\n", sourceURI, targetURI);
                                    return true;
                                }
                            }
                        }
                    }
                    return false;
                })
                .map(AuthorizationDecision::new)
                .defaultIfEmpty(new AuthorizationDecision(false));
    }

}

3、ResourceServerConfig

@AllArgsConstructor
@Configuration
@EnableWebFluxSecurity
public class ResourceServerConfig {
    private final AuthorizationManager authorizationManager;
    private final SecurityProperties securityProperties;
    private final RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    private final RestAuthenticationEntryPoint restAuthenticationEntryPoint;
    // 暂时不能用
//    private final WhiteListRemoveJwtFilter ignoreUrlsRemoveJwtFilter;

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        // jwt 增加
        http.oauth2ResourceServer().jwt()
                .jwtAuthenticationConverter(jwtAuthenticationConverter());
        //自定义处理JWT请求头过期或签名错误的结果
        http.oauth2ResourceServer().authenticationEntryPoint(restAuthenticationEntryPoint);
        //对白名单路径,直接移除JWT请求头
//        http.addFilterBefore(ignoreUrlsRemoveJwtFilter, SecurityWebFiltersOrder.AUTHENTICATION);
        // 白名单
        List<String> whiteList = securityProperties.getWhiteList();
        if (!CollectionUtils.isEmpty(whiteList)) {
            //白名单配置
            http.authorizeExchange().pathMatchers(ArrayUtil.toArray(whiteList, String.class)).permitAll();
        }
        // 要权限的
        List<String> needCheck = securityProperties.getNeedCheck();
        http.authorizeExchange()
                .pathMatchers(ArrayUtil.toArray(needCheck, String.class))
                .authenticated()
                .anyExchange().access(authorizationManager)//鉴权管理器配置
                .and().exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler)//处理未授权
                .authenticationEntryPoint(restAuthenticationEntryPoint)//处理未认证
                .and().csrf().disable();
        return http.build();
    }

    @Bean
    public Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        jwtGrantedAuthoritiesConverter.setAuthorityPrefix(AuthConstant.AUTHORITY_PREFIX);
        jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(AuthConstant.AUTHORITY_CLAIM_NAME);
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
        return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
    }

}

4、配置文件

spring:
  application:
    name: learn-cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
      routes:
        - id: public-auth
          # lb代表从注册中心获取服务,且已负载均衡方式转发
          uri: lb://learn-shop-public-auth
          predicates:
            - Path=/public-auth/**
          # 加上StripPrefix=1,否则转发到后端服务时会带上consumer前缀
          filters:
            - StripPrefix=1
        - id: admin-system
          # lb代表从注册中心获取服务,且已负载均衡方式转发
          uri: lb://learn-shop-admin-system
          predicates:
            - Path=/admin-system/**
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: 'http://localhost:8999/rsa/publicKey' # 获取公钥

# 自己定义安全配置
secure:
  client: # 登陆客户端配置
    client-id: webapp
    client-secret: webapp
    scope: webapp
    grant-type: password
    access-token-uri: "http://learn-shop-public-auth/oauth/token"
  white-list:
    - "/actuator/**"              # 健康检查
    - "/userApi/*"                # 获取用户信息
    - "/public-auth/oauth/token"  # 获取token或者刷新token
  need-check:
    - "/**/*Api/**"
    - "/*Api/**"

源码地址:看这里,看这里,看这里

测试请参考
Spring Cloud 之 Zuul、Spring Security、Oauth2 的整合

  • 5
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: Spring Cloud Gateway可以通过整合OAuth2来实现安全认证和授权功能。具体步骤如下: 1. 引入Spring SecuritySpring Security OAuth2依赖。 2. 配置Spring SecurityOAuth2的相关配置,包括认证服务器地址、客户端ID和密钥等。 3. 在Spring Cloud Gateway中配置路由规则,并添加安全过滤器,将请求转发到认证服务器进行认证和授权。 4. 在路由规则中添加需要进行安全认证和授权的路径和方法。 5. 在授权成功后,将令牌添加到请求头中,以便后续服务可以获取到令牌信息。 通过以上步骤,就可以实现Spring Cloud Gateway的安全认证和授权功能。 ### 回答2: Spring Cloud Gateway是一个轻量级的API网关,而OAuth2是一种基于授权的安全协议,使用Spring Cloud Gateway整合OAuth2可以为API提供更安全的保护。 首先,我们需要在应用程序中引入OAuth2依赖,如下所示: ``` <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> <version>2.3.5.RELEASE</version> </dependency> ``` 接下来,我们需要配置OAuth2具体的认证授权过程。在application.yml文件中进行配置,如下所示: ``` spring: security: oauth2: client: registration: client1: client-id: client1 client-secret: secret1 scope: read,write client-authentication-method: post authorization-grant-type: authorization_code redirect-uri: http://localhost:8081/oauth2/callback client-name: Client 1 provider: provider1 ``` 上述配置中,我们设置了客户端ID、秘钥、范围、客户端认证方式、授权类型等必要参数。其中,“authorization_code”是OAuth2的一种授权类型,通过授权码来获取令牌,这是最常用的一种授权类型。 此外,我们还需要在Gateway中进行相关的配置,包括路由规则、认证过滤器、访问权限控制等。具体实现可以参考springcloudgateway的官方文档。 总的来说,使用Spring Cloud Gateway整合OAuth2可以为API提供更安全的保护,同时也为开发人员提供了更多的灵活性和高效性。 ### 回答3: Spring Cloud GatewaySpring Cloud的一个新项目,它基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术,提供了一种构建API网关的方式。OAuth2是一种基于标准的授权协议,允许客户端(包括第三方应用程序和用户代理)访问受保护的资源。那么,如何在Spring Cloud Gateway中集成OAuth2呢? 首先,我们需要在Spring Cloud Gateway中引入spring-cloud-starter-oauth2依赖。然后,在application.yml配置文件中添加以下配置: ``` spring: security: oauth2: client: registration: custom-client: provider: custom-provider clientId: custom-client-id clientSecret: custom-client-secret authorizationGrantType: authorization_code redirectUriTemplate: "{baseUrl}/login/oauth2/code/{registrationId}" scope: - read - write provider: custom-provider: authorizationUri: https://custom-provider.com/oauth2/authorize tokenUri: https://custom-provider.com/oauth2/token userInfoUri: https://custom-provider.com/oauth2/userinfo userNameAttribute: sub ``` 上面的配置中,我们定义了一个名为custom-client的客户端,该客户端使用了我们自定义的OAuth2提供程序custom-provider。在这里,我们还定义了客户端ID和客户端密码,以及授权类型和重定向URI。此外,我们还定义了客户端的作用域和OAuth2提供程序的授权URI、令牌URI和用户信息URI。 在配置完成后,我们需要为Spring Cloud Gateway定义一个OAuth2路由过滤器。可以使用spring-security-oauth2-autoconfigure自动配置这个过滤器。在application.yml中添加以下配置: ``` spring: cloud: gateway: default-filters: - OAuth2GatewayFilterFactory ``` 注意,OAuth2GatewayFilterFactory是spring-security-oauth2-autoconfigure自动配置的名称。 现在,我们可以在Spring Cloud Gateway的路由定义中使用OAuth2路由过滤器了。例如: ``` spring: cloud: gateway: routes: - id: resource-service uri: http://localhost:8080 predicates: - Path=/resource/** filters: - OAuth2=custom-client ``` 在上面的路由定义中,我们使用了自定义OAuth2客户端custom-client。这将强制执行OAuth2认证,以确保只有已验证用户才能访问资源。 最后,我们需要创建一个OAuth2登录页面。使用spring-security-oauth2-autoconfigure依赖可以自动创建登录页面。在application.yml中添加以下配置: ``` spring: security: oauth2: client: registration: custom-client: clientName: Custom Client ``` 上面的配置中,我们定义了客户端的名称为Custom Client。当用户尝试访问受保护的资源时,系统将调用OAuth2登录页面并要求它输入凭据。 这就是在Spring Cloud Gateway中集成OAuth2的过程。通过使用Spring Cloud GatewayOAuth2,我们可以轻松地构建和保护API网关,实现更好的安全性和可扩展性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值