微服务技术系列教程(41)- SpringCloud -OAuth2搭建微服务开放平台

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 微服务技术系列教程(41)- SpringCloud -OAuth2搭建微服务开放平台

引言

在Spring Cloud需要使用oauth2来实现多个微服务的统一认证授权,通过向OAuth服务发送某个类型的grant type进行集中认证和授权,从而获得access_token,而这个token是受其他微服务信任的,我们在后续的访问可以通过access_token来进行,从而实现了微服务的统一认证授权。

客户端根据约定的ClientID、ClientSecret、Scope来从Access Token URL地址获取AccessToken,并经过AuthURL认证,用得到的AccessToken来访问其他资源接口。

Spring Cloud OAuth2 需要依赖Spring Security。

OAuth2角色划分:

  • 「Resource Server」:被授权访问的资源
  • 「Authotization Server」:OAuth2认证授权中心
  • 「Resource Owner」: 用户
  • 「Client」:使用API的客户端(如Android 、IOS、web app)

OAuth2四种授权方式:

  • 授权码模式(authorization code):用在客户端与服务端应用之间授权
  • 简化模式(implicit):用在移动app或者web app(这些app是在用户的设备上的,如在手机上调起微信来进行认证授权)
  • 密码模式(resource owner password credentials):应用直接都是受信任的(都是由一家公司开发的)
  • 客户端模式(client credentials):用在应用API访问

2. OAuth2 环境搭建

2.1 认证授权中心服务

2.1.1 密码模式

1.添加maven依赖:

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.1.RELEASE</version>
</parent>
<!-- 管理依赖 -->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Finchley.M7</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
<dependencies>
  <!-- SpringBoot整合Web组件 -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
  </dependency>
  <!-- springboot整合freemarker -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
  </dependency>
  <!-->spring-boot 整合security -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
  </dependency>
  <!-- spring-cloud-starter-oauth2 -->
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
  </dependency>
</dependencies>
<!-- 注意: 这里必须要添加, 否者各种依赖有问题 -->
<repositories>
  <repository>
    <id>spring-milestones</id>
    <name>Spring Milestones</name>
    <url>https://repo.spring.io/libs-milestone</url>
    <snapshots>
      <enabled>false</enabled>
    </snapshots>
  </repository>
</repositories>

2.创建授权配置信息

// 配置授权中心信息
@Configuration
@EnableAuthorizationServer // 开启认证授权中心
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
  // accessToken有效期
  private int accessTokenValiditySeconds = 7200; // 两小时
  // 添加商户信息
  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    // withClient appid
    clients.inMemory().withClient("client_1").secret(passwordEncoder().encode("123456"))
        .authorizedGrantTypes("password","client_credentials","refresh_token").scopes("all").accessTokenValiditySeconds(accessTokenValiditySeconds);
  }
  // 设置token类型
  public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints.authenticationManager(authenticationManager()).allowedTokenEndpointRequestMethods(HttpMethod.GET,
        HttpMethod.POST);
  }
  @Override
  public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
    // 允许表单认证
    oauthServer.allowFormAuthenticationForClients();
    // 允许check_token访问
    oauthServer.checkTokenAccess("permitAll()");
  }
  @Bean
  AuthenticationManager authenticationManager() {
    AuthenticationManager authenticationManager = new AuthenticationManager() {
      public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        return daoAuhthenticationProvider().authenticate(authentication);
      }
    };
    return authenticationManager;
  }
  @Bean
  public AuthenticationProvider daoAuhthenticationProvider() {
    DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
    daoAuthenticationProvider.setUserDetailsService(userDetailsService());
    daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
    daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
    return daoAuthenticationProvider;
  }
  // 设置添加用户信息,正常应该从数据库中读取
  @Bean
  UserDetailsService userDetailsService() {
    InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
    userDetailsService.createUser(User.withUsername("user_1").password(passwordEncoder().encode("123456"))
        .authorities("ROLE_USER").build());
    userDetailsService.createUser(User.withUsername("user_2").password(passwordEncoder().encode("1234567"))
        .authorities("ROLE_USER").build());
    return userDetailsService;
  }
  @Bean
  PasswordEncoder passwordEncoder() {
    // 加密方式
    PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    return passwordEncoder;
  }
}

3.启动授权服务

@SpringBootApplication
public class AppOauth2 {
  public static void main(String[] args) {
    SpringApplication.run(AppOauth2.class, args);
  }
}

4.获取accessToken请求地址: http://localhost:8080/oauth/token

5.验证accessToken是否有效:http://localhost:8080/oauth/check_token?token=b212eaec-63a7-489d-b5a2-883ec248c417

6.刷新新的accessToken:http://localhost:8080/oauth/token?grant_type=refresh_token&refresh_token=4803dbfe-41c8-417c-834e-6be6b296b767&client_id=client_1&client_secret=123456,需要配置:

public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints.authenticationManager(authenticationManager()).allowedTokenEndpointRequestMethods(HttpMethod.GET,
        HttpMethod.POST);
    endpoints.authenticationManager(authenticationManager());
    endpoints.userDetailsService(userDetailsService());
  }
2.1.2 授权模式

1.新增授权权限

public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    // withClient appid
    clients.inMemory().withClient("client_1").secret(passwordEncoder().encode("123456"))
        .authorizedGrantTypes("password", "client_credentials", "refresh_token", "authorization_code")
        .scopes("all").redirectUris("http://www.xxx.com")
        .accessTokenValiditySeconds(accessTokenValiditySeconds)
        .refreshTokenValiditySeconds(refreshTokenValiditySeconds);
  }

2.请求http://localhost:8080/oauth/authorize?response_type=code&client_id=client_1&redirect_uri=http://www.xxx.com ,访问报错:

User must be authenticated with Spring Security before authorization can be completed.

3.解决办法 添加Security权限

@Component
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  // 授权中心管理器
  @Bean
  @Override
  public AuthenticationManager authenticationManagerBean() throws Exception {
    AuthenticationManager manager = super.authenticationManagerBean();
    return manager;
  }
  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }
  // 拦截所有请求,使用httpBasic方式登陆
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().antMatchers("/**").fullyAuthenticated().and().httpBasic();
  }
}

2.2 资源服务端

一个资源服务器,各个服务之间的通信(访问需要权限的资源)时需携带访问令牌

资源服务器通过 @EnableResourceServer 注解来开启一个 OAuth2AuthenticationProcessingFilter 类型的过滤器,通过继承 ResourceServerConfigurerAdapter 类来配置资源服务器。

1.添加maven依赖

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.1.RELEASE</version>
</parent>
<!-- 管理依赖 -->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Finchley.M7</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
<dependencies>
  <!-- SpringBoot整合Web组件 -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
  </dependency>
  <!-- springboot整合freemarker -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
  </dependency>
  <!-->spring-boot 整合security -->
  <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>
</dependencies>
<!-- 注意: 这里必须要添加, 否者各种依赖有问题 -->
<repositories>
  <repository>
    <id>spring-milestones</id>
    <name>Spring Milestones</name>
    <url>https://repo.spring.io/libs-milestone</url>
    <snapshots>
      <enabled>false</enabled>
    </snapshots>
  </repository>
</repositories>

2.application.yml

server:
  port: 8081
logging:
  level:
    org.springframework.security: DEBUG
security:
  oauth2:
    resource:
      ####从认证授权中心上验证token
      tokenInfoUri: http://localhost:8080/oauth/check_token
      preferTokenInfo: true
    client:
      accessTokenUri: http://localhost:8080/oauth/token
      userAuthorizationUri: http://localhost:8080/oauth/authorize
      ###appid
      clientId: client_1
      ###appSecret
      clientSecret: 123456

3.资源拦截配置

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
  @Override
  public void configure(HttpSecurity http) throws Exception {
    // 对 api/order 请求进行拦截
    http.authorizeRequests().antMatchers("/api/order/**").authenticated();
  }
}

4.资源服务请求

@RestController
@RequestMapping("/api/order")
public class OrderController {
  @RequestMapping("/addOrder")
  public String addOrder() {
    return "addOrder";
  }
}

5.启动权限

@SpringBootApplication
@EnableOAuth2Sso
public class AppOrder {
  public static void main(String[] args) {
    SpringApplication.run(AppOrder.class, args);
  }
}

6.资源访问,请求资源: http://127.0.0.1:8081/api/order/addOrder

Authorization: bearer 31820c84-2e52-408f-9d21-a62483aad59d

3. 将应用信息改为数据库存储

官方推荐SQL:https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql

1.添加maven依赖:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
</dependency>

2.application.yml

spring:
  datasource:
    hikari:
      connection-test-query: SELECT 1
      minimum-idle: 1
      maximum-pool-size: 5
      pool-name: dbcp1
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/alan-oauth?autoReconnect=true&useSSL=false
    username: root
    password: 123456

3.修改配置文件类

// 配置授权中心信息
@Configuration
@EnableAuthorizationServer // 开启认证授权中心
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
  @Autowired
  @Qualifier("authenticationManagerBean")
  private AuthenticationManager authenticationManager;
  @Autowired
  @Qualifier("dataSource")
  private DataSource dataSource;
  // @Autowired
  // private UserDetailsService userDetailsService;
  @Bean
  public TokenStore tokenStore() {
    // return new InMemoryTokenStore(); //使用内存中的 token store
    return new JdbcTokenStore(dataSource); /// 使用Jdbctoken store
  }
  @Override
  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    // 添加授权用户
    clients.jdbc(dataSource);
//    .withClient("client_1").secret(new BCryptPasswordEncoder().encode("123456"))
//    .authorizedGrantTypes("password", "refresh_token", "authorization_code")// 允许授权范围
//    .redirectUris("http://www.xxx.com").authorities("ROLE_ADMIN", "ROLE_USER")// 客户端可以使用的权限
//    .scopes("all").accessTokenValiditySeconds(7200).refreshTokenValiditySeconds(7200);
  }
  @Override
  public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    endpoints.tokenStore(tokenStore()).authenticationManager(authenticationManager)
        .userDetailsService(userDetailsService());// 必须设置
                              // UserDetailsService
                              // 否则刷新token 时会报错
  }
  @Bean
  UserDetailsService userDetailsService() {
    InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
    userDetailsService.createUser(User.withUsername("user_1").password(new BCryptPasswordEncoder().encode("123456"))
        .authorities("ROLE_USER").build());
    userDetailsService.createUser(User.withUsername("user_2")
        .password(new BCryptPasswordEncoder().encode("1234567")).authorities("ROLE_USER").build());
    return userDetailsService;
  }
  @Override
  public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()")
        .allowFormAuthenticationForClients();// 允许表单登录
  }
}


相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
30天前
|
Kubernetes Cloud Native 微服务
探索云原生技术:容器化与微服务架构的融合之旅
本文将带领读者深入了解云原生技术的核心概念,特别是容器化和微服务架构如何相辅相成,共同构建现代软件系统。我们将通过实际代码示例,探讨如何在云平台上部署和管理微服务,以及如何使用容器编排工具来自动化这一过程。文章旨在为开发者和技术决策者提供实用的指导,帮助他们在云原生时代中更好地设计、部署和维护应用。
|
11天前
|
JSON Java API
利用Spring Cloud Gateway Predicate优化微服务路由策略
Spring Cloud Gateway 的路由配置中,`predicates`​(断言)用于定义哪些请求应该匹配特定的路由规则。 断言是Gateway在进行路由时,根据具体的请求信息如请求路径、请求方法、请求参数等进行匹配的规则。当一个请求的信息符合断言设置的条件时,Gateway就会将该请求路由到对应的服务上。
112 69
利用Spring Cloud Gateway Predicate优化微服务路由策略
|
30天前
|
Java 开发者 微服务
从单体到微服务:如何借助 Spring Cloud 实现架构转型
**Spring Cloud** 是一套基于 Spring 框架的**微服务架构解决方案**,它提供了一系列的工具和组件,帮助开发者快速构建分布式系统,尤其是微服务架构。
160 69
从单体到微服务:如何借助 Spring Cloud 实现架构转型
|
12天前
|
SpringCloudAlibaba JavaScript Dubbo
【SpringCloud Alibaba系列】Dubbo dubbo-admin安装教程篇
本文介绍了 Dubbo-Admin 的安装和使用步骤。Dubbo-Admin 是一个前后端分离的项目,前端基于 Vue,后端基于 Spring Boot。安装前需确保开发环境(Windows 10)已安装 JDK、Maven 和 Node.js,并在 Linux CentOS 7 上部署 Zookeeper 作为注册中心。
【SpringCloud Alibaba系列】Dubbo dubbo-admin安装教程篇
|
27天前
|
Java Nacos Sentinel
Spring Cloud Alibaba:一站式微服务解决方案
Spring Cloud Alibaba(简称SCA) 是一个基于 Spring Cloud 构建的开源微服务框架,专为解决分布式系统中的服务治理、配置管理、服务发现、消息总线等问题而设计。
215 13
Spring Cloud Alibaba:一站式微服务解决方案
|
14天前
|
Java 关系型数据库 Nacos
微服务SpringCloud链路追踪之Micrometer+Zipkin
SpringCloud+Openfeign远程调用,并用Mircrometer+Zipkin进行链路追踪
130 20
|
2天前
|
Java 关系型数据库 数据库
微服务SpringCloud分布式事务之Seata
SpringCloud+SpringCloudAlibaba的Seata实现分布式事务,步骤超详细,附带视频教程
15 1
|
1月前
|
负载均衡 Java 开发者
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
128 5
|
2月前
|
Kubernetes Java 微服务
微服务上下线动态感知实现的技术解析
随着微服务架构的广泛应用,服务的动态管理和监控变得尤为重要。在微服务架构中,服务的上下线是一个常见的操作,如何实时感知这些变化,确保系统的稳定性和可靠性,成为了一个关键技术挑战。本文将深入探讨微服务上下线动态感知的实现方式,从技术基础、场景案例、解决思路和底层原理等多个维度进行阐述,并分别使用Java和Python进行演示介绍。
67 4
|
2月前
|
运维 Kubernetes Docker
深入理解容器化技术及其在微服务架构中的应用
深入理解容器化技术及其在微服务架构中的应用
66 1