如何在spring cloud zuul里通过ory hydra进行oauth2认证

背景说明

zuul在spring cloud里常用于API网关接入,用于对外提供统一服务接口,为外部访问提供鉴权等安全控制,保护内部各微服务资源。而ory hydra是一个开源的轻量化认证管理中心,支持oauth2规范标准,具体可参考这里。我们通过spring security来实现两者的关联,将zuul作为一个resource server, 让其调用认证中心hydra的oauth2 token校验接口来验证请求是否合法。下面按步骤说明实施过程。

准备工作

首先需在hydra里添加oauth2记录。这里的hydra通过docker-compose安装的,4445是hydra的管理端口。通过如下指令添加一个记录: client_id是id_pkce, 支持PKCE认证模式,所以token-endpoint-auth-method为none。

docker-compose -f quickstart.yml exec hydra \
    hydra clients create \
    --endpoint http://127.0.0.1:4445/ \
    --allowed-cors-origins http://localhost \
    --id id_pkce \
    --grant-types authorization_code,refresh_token \
    --response-types code,id_token \ \
    --scope openid,offline,first_party \ \
    --callbacks http://localhost/auth/ \
    --token-endpoint-auth-method none

实施步骤

pom.xml文件

这里列出关键的几个依赖

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>	
	</properties>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-oauth2</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	<dependencies>	

参数配置

分两部分,一个是zuul的路由配置,另一个访问oauth2认证中心hydra所需的参数配置。

zuul配置

这里假设有两个路径转发配置,/api/svc1/开头的转微服务msc-1,/api/svc2/开头的转外部连接服务

zuul:
  routes:
    svc1:
      path: /api/svc1/**
      service-id: msc-1
    svc2:
      path: /api/svc2/**
      url: http://192.168.0.1:10000/api/

oauth2配置

这里的配置用于连接hydra用。192.168.0.2是hydra验证中心的内网IP,端口5000是管理端口4445的外部映射。为安全起见,hydra的管理端口只能内部访问,不能被外部访问。/oauth2/introspect就是hydra的校验token的restful接口

security:
  oauth2:
    client:
      client-id: id_pkce
      client-secret: "{noop}"
    resource:
      token-info-uri: http://192.168.0.2:5000/oauth2/introspect      

编写WebSecurity类

这里配置所有请求都需经过验证

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http.cors()
            .and()
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .csrf().disable()
        ;
        // @formatter:on
    }
}

编写ResourceServer类

首先定义类

@Configuration
@EnableResourceServer
@Slf4j
public class ResServerConfig extends ResourceServerConfigurerAdapter{

然后实现访问hydra, 注入前面定义的oauth2参数,通过RemoteTokenServices实现。

    @Value("${security.oauth2.client.client-id}")
    private String clientId;

    @Value("${security.oauth2.client.client-secret}")
    private String clientSecret;

    @Value("${security.oauth2.resource.token-info-uri}")
    private String tokenCheckUrl;

    @Autowired
    RemoteTokenServices remoteTokenServices;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        remoteTokenServices.setClientId(clientId);
        remoteTokenServices.setClientSecret(clientSecret);
        remoteTokenServices.setCheckTokenEndpointUrl(tokenCheckUrl);
        resources.tokenServices(remoteTokenServices);
    }

接着配置哪些请求需走oauth2校验,这里定义所有/api开头的请求都要有scope"first_party"。这是前面在hydra创建的oauth2记录的属性之一。

   @Override
    public void configure(HttpSecurity http) throws Exception {
        final String[] urlPattern = {"/api/**"};
        http
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .requestMatchers().antMatchers(urlPattern)
            .and()
            .authorizeRequests()
            .antMatchers(urlPattern)
            .access("#oauth2.hasScope('first_party')")
        ;
    }
}

测试验证

  1. 做一个get请求/api/showData,通过RestController实现
    @RequestMapping({"/api/showData","/api/**"})
    public Object showData(){
        Map<String,String> map = new HashMap<String,String>();
        map.put("t1","this is test1");
        map.put("t2","this is test2");
        return map;
    }
  1. 直接请求,会返回401错误 401错误:unauthorized

  2. 带错误的Bearer token, 返回401错误
    401错误:invalid_token

  3. 带正确的Bearer token, 返回200 OK
    200: OK

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值