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

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: 微服务技术系列教程(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();// 允许表单登录
  }
}


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
4天前
|
消息中间件 Java RocketMQ
Spring Cloud RocketMQ:构建可靠消息驱动的微服务架构
【4月更文挑战第28天】消息队列在微服务架构中扮演着至关重要的角色,能够实现服务之间的解耦、异步通信以及数据分发。Spring Cloud RocketMQ作为Apache RocketMQ的Spring Cloud集成,为微服务架构提供了可靠的消息传输机制。
15 1
|
7天前
|
Java 数据安全/隐私保护 Sentinel
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
|
7天前
|
运维 监控 Java
SpringCloud&认识微服务
SpringCloud&认识微服务
11 0
|
14天前
|
人工智能 监控 安全
Java+Spring Cloud +Vue+UniApp微服务智慧工地云平台源码
视频监控系统、人员实名制与分账制管理系统、车辆管理系统、环境监测系统、大型设备监测(龙门吊、塔吊、升降机、卸料平台等)、用电监测系统、基坑监测系统、AI算法分析(安全帽佩戴、火焰识别、周界报警、人员聚众报警、升降机超载报警)、安全培训、设备监测。
20 4
|
15天前
|
负载均衡 Java 开发者
细解微服务架构实践:如何使用Spring Cloud进行Java微服务治理
【4月更文挑战第17天】Spring Cloud是Java微服务治理的首选框架,整合了Eureka(服务发现)、Ribbon(客户端负载均衡)、Hystrix(熔断器)、Zuul(API网关)和Config Server(配置中心)。通过Eureka实现服务注册与发现,Ribbon提供负载均衡,Hystrix实现熔断保护,Zuul作为API网关,Config Server集中管理配置。理解并运用Spring Cloud进行微服务治理是现代Java开发者的关键技能。
|
23天前
|
消息中间件 监控 Java
微服务技术发展
微服务技术发展
|
24天前
|
SpringCloudAlibaba Java 数据库
SpringCloud Alibaba微服务 -- Seata的原理和使用
SpringCloud Alibaba微服务 -- Seata的原理和使用
|
24天前
|
SpringCloudAlibaba 监控 Java
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
|
24天前
|
SpringCloudAlibaba Java API
SpringCloud Alibaba微服务工程搭建(保姆级)
SpringCloud Alibaba微服务工程搭建(保姆级)
|
24天前
|
SpringCloudAlibaba Java Nacos
SpringCloud Alibaba微服务 -- Nacos使用以及注册中心和配置中心的应用(保姆级)
SpringCloud Alibaba微服务 -- Nacos使用以及注册中心和配置中心的应用(保姆级)