目录

前言

《Spring Microservices in Action》

《Spring Cloud Alibaba 微服务原理与实战》

《B站 尚硅谷 SpringCloud 框架开发教程 周阳》

OAuth2 是一个基于令牌的安全验证和授权框架。他允许用户使用第三方验证服务进行验证。 如果用户成功进行了验证, 则会出示一个令牌,该令牌必须与每个请求一起发送。然后,验证服务可以对令牌进行确认;


1. OAuth2 基础知识

1.1 安全性的 4 个组成部分

  • 受保护资源:Resource Server,开发人员想要保护的资源(如一个微服务),需要确保只有已通过验证并且具有适当授权的用户才能访问它;
  • 资源所有者:Resource Owner,资源所有者定义哪些应用程序可以调用其服务,哪些用户可以访问该服务,以及他们可以使用该服务完成哪些事情。 资源所有者注册的每个应用程序都将获得一个应用程序名称,该应用程序名称与应用程序密钥一起标识应用程序。 应用程序名称和密钥的组合是在验证 OAuth2 令牌时传递的凭据的一部分;
  • 应用程序:Client,这是代表用户调用服务的应用程序。毕竟,用户很少直接调用服务 。相反,他们依赖应用程序为他们工作。
  • OAuth2 验证服务器:Authorization Server,OAuth2 验证服务器是应用程序和正在使用的服务之间的中间人。 OAuth2 验证服务器允许用户对自己进行验证,而不必将用户凭据传递给由应用程序代表用户调用的每个服务;

1.2 OAuth2 的工作原理

  • 第三方客户端资源所有者(用户)申请认证请求;
  • 【关键】用户同意请求,返回一个许可;
  • 客户端根据许可向认证服务器申请认证令牌 Token;
  • 客户端根据认证令牌向资源服务器申请相关资源;

1.3 OAuth2 规范的 4 种类型的授权

  • 密码( password ) ;
  • 客户端凭据( client credential ) ;;
  • 授权码( authorization code) ;
  • 隐式( imp licit );

1.4 OAuth2 的优势

  • 允许开发人员轻松与第三方云服务提供商集成,并使用这些服务进行用户验证和授权,而无须不断地将用户的凭据传递给第三方服务;

1.5 OAuth2 核心原理

  • 先有一个 OAuth2 认证服务器,用来创建和管理 OAuth2 访问令牌;
  • 接着在受保护资源主程序类上添加一个注解:@EnableResourceServer,该注解会强制执行一个过滤器,该过滤器会拦截对服务的所有传入调用,检查传入调用的 HTTP 首部中是否存在 OAuth2 访问令牌,然后调用 security.oauth2.resource.userInfoUri 中定义的回调 URL 告诉客户端与 OAuth2 认证服务器交互,查看令牌是否有效;
  • 一旦获悉令牌是有效的,@EnableResourceServer 注解也会应用任何访问控制规则,以控制什么人可以访问服务;

1.6 JSON Web Token

2. 建立 OAuth2 服务器

  • 验证服务将验证用户凭据并颁发令牌;
  • 每当用户尝试访问由,如正服务保护的服务时,验证服务将确认 OAuth2 令牌是否已由其颁发并且尚未过期;

2.1 引入 pom.xml 依赖文件

  1. <!--security 通用安全库-->
  2. <dependency>
  3. <groupid>org.springframework.cloud</groupid>
  4. <artifactid>spring-cloud-security</artifactid>
  5. </dependency>
  6. <!--oauth2.0-->
  7. <dependency>
  8. <groupId>org.springframework.cloud</groupId>
  9. <artifactId>spring-cloud-starter-oauth2</artifactId>
  10. </dependency>

2.2 主程序类上添加注解

  • @EnableAuthorizationServer:该服务将作为 OAuth2 服务;
  • @EnableResourceServer:表示该服务是受保护资源;(该注解在 3.3 详解)

2.3 添加受保护对象的端点

在 controller 包下;

  • 该端点将映射到 /auth/user 端点,当受保护的服务调用 /auth/user 时,将会确认 OAuth2 访问令牌,并检索发文手背欧虎服务所分配的角色;
  1. /**
  2. * 用户信息校验
  3. * 由受保护服务调用,确认 OAuth2 访问令牌,并检索访问受保护服务的用户所分配的角色
  4. * @param OAuth2Authentication 信息
  5. * @return 用户信息
  6. */
  7. @RequestMapping(value = { "/user" }, produces = "application/json")
  8. public Map<String, Object> user(OAuth2Authentication user) {
  9. Map<String, Object> userInfo = new HashMap<>();
  10. userInfo.put("user", user.getUserAuthentication().getPrincipal());
  11. userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
  12. return userInfo;
  13. }

2.4 定义哪些应用程序可以使用服务

在 config 包下;

  • ClientDetailsServiceConfigurer 支持两种类型的储存:内存存储和JDBC存储,如下分点所示:

2.4.1 使用 JDBC 存储

  • OAuth2Config 类
  1. @Configuration
  2. //继承 AuthorizationServerConfigurerAdapter 类
  3. public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
  4. @Autowired
  5. private DataSource dataSource;
  6. @Autowired
  7. private AuthenticationManager authenticationManager;
  8. @Autowired
  9. private UserDetailsService userDetailsService;
  10. @Override
  11. //定义哪些客户端将注册到服务
  12. public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  13. //JDBC存储:
  14. JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
  15. clientDetailsService.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT); //设置我们的自定义的sql查找语句
  16. clientDetailsService.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT); //设置我们的自定义的sql查找语句
  17. clients.withClientDetails(clientDetailsService); //从 jdbc 查出数据来存储
  18. }
  19. @Override
  20. //使用 Spring 提供的默认验证管理器和用户详细信息服务
  21. public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
  22. endpoints
  23. .authenticationManager(authenticationManager)
  24. .userDetailsService(userDetailsService);
  25. }
  26. }
  • SecurityConstants 类:里面存放上述提到的 SQL 查询语句;
  1. public interface SecurityConstants {
  2. /**
  3. * sys_oauth_client_details 表的字段,不包括client_id、client_secret
  4. */
  5. String CLIENT_FIELDS = "client_id, client_secret, resource_ids, scope, "
  6. + "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, "
  7. + "refresh_token_validity, additional_information, autoapprove";
  8. /**
  9. *JdbcClientDetailsService 查询语句
  10. */
  11. String BASE_FIND_STATEMENT = "select " + CLIENT_FIELDS + " from sys_oauth_client_details";
  12. /**
  13. * 默认的查询语句
  14. */
  15. String DEFAULT_FIND_STATEMENT = BASE_FIND_STATEMENT + " order by client_id";
  16. /**
  17. * 按条件client_id 查询
  18. */
  19. String DEFAULT_SELECT_STATEMENT = BASE_FIND_STATEMENT + " where client_id = ?";
  20. }

2.4.2 使用内存储存

  1. @Configuration
  2. //继承 AuthorizationServerConfigurerAdapter 类
  3. public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
  4. @Autowired
  5. private AuthenticationManager authenticationManager;
  6. @Autowired
  7. private UserDetailsService userDetailsService;
  8. @Override
  9. //定义哪些客户端将注册到服务
  10. public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  11. clients.inMemory()
  12. .withClient("eagleeye") //名称
  13. .secret("thisissecret") //密钥
  14. .authorizedGrantTypes("refresh_token", "password", "client_credentials") //授权类型列表
  15. .scopes("webclient", "mobileclient"); //获取访问令牌时可以操作的范围
  16. }
  17. @Override
  18. //使用 Spring 提供的默认验证管理器和用户详细信息服务
  19. public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
  20. endpoints
  21. .authenticationManager(authenticationManager)
  22. .userDetailsService(userDetailsService);
  23. }
  24. }

2.5 为应用程序定义用户 ID、密码和角色

在 config 包下:

  • 可以从内存数据存储、支持 JDBC 的关系数据库或 LDAP 服务器中存储和检索用户信息;
  1. @Configuration
  2. @EnableWebSecurity
  3. //扩展核心 Spring Security 的 WebSecurityConfigurerAdapter
  4. public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
  5. //用来处理验证
  6. @Override
  7. @Bean
  8. public AuthenticationManager authenticationManagerBean() throws Exception {
  9. return super.authenticationManagerBean();
  10. }
  11. //处理返回用户信息
  12. @Override
  13. @Bean
  14. public UserDetailsService userDetailsServiceBean() throws Exception {
  15. return super.userDetailsServiceBean();
  16. }
  17. //configure() 方法定义用户、密码与角色
  18. @Override
  19. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  20. auth
  21. .inMemoryAuthentication()
  22. .withUser("john.carnell").password("password1").roles("USER")
  23. .and()
  24. .withUser("william.woodward").password("password2").roles("USER", "ADMIN");
  25. }
  26. }
  • 上述例子中 john.carnell 用户拥有 USER 用户;
  • william.woodward 拥有 ADMIN 用户;

2.6 通过发送 POST 请求验证用户

  • 发送:POST http://localhost:8901/auth/oauth/token
  • 并在 POST 的请求体里带上应用程序名称、密钥、用户 ID 和密码,可以模拟用户获取 OAuth2 令牌;

3. 使用 OAuth2 建立并保护服务资源

  • 创建和管理 OAuth2 访问令牌是 OAuth2 服务器的职责;
  • 定义哪些用户角色有权执行哪些操作在单个服务级别上的;

3.1 引入 pom.xml 依赖文件

  1. <!--security 通用安全库-->
  2. <dependency>
  3. <groupid>org.springframework.cloud</groupid>
  4. <artifactid>spring-cloud-security</artifactid>
  5. </dependency>
  6. <!--oauth2.0-->
  7. <dependency>
  8. <groupId>org.springframework.security.oauth</groupId>
  9. <artifactId>spring-security-oauth2</artifactId>
  10. </dependency>

3.2 添加 bootstrap.yml 配置文件

  1. security:
  2. oauth2:
  3. resource:
  4. userInfoUri: http://localhost:8901/auth/user
  • 这里添加回调 URL,客户端访问受保护服务时,受保护服务将调用 /auth/user 端点,向 OAuth2 服务器检查访问令牌是否生效;

3.3 在主程序类上添加注解

  • @EnableResourceServer:表示该服务是受保护资源;
  • 该注解会强制执行一个过滤器,该过滤器会拦截对服务的所有传入调用,检查传入调用的 HTTP 首部中是否存在 OAuth2 访问令牌,然后调用 security.oauth2.resource.userInfoUri 中定义的回调 URL 来查看令牌是否有效;
  • 一旦获悉令牌是有效的,@EnableResourceServer 注解也会应用任何访问控制规则,以控制什么人可以访问服务;
  1. @SpringBootApplication
  2. @EnableEurekaClient
  3. @EnableCircuitBreaker //断路器
  4. @EnableResourceServer //表示受保护资源
  5. public class Application {
  6. //注入一个过滤器,会拦截对服务的所有传入调用
  7. @Bean
  8. public Filter userContextFilter() {
  9. UserContextFilter userContextFilter = new UserContextFilter();
  10. return userContextFilter;
  11. }
  12. public static void main(String[] args) {
  13. SpringApplication.run(Application.class, args);
  14. }
  15. }

3.4 定义访问控制规则

在 config 包或 security 包下;

  • 要定义访问控制规则,需要扩展 ResourceServerConfigurerAdapter 类井覆盖 configure() 方法;
  • 有多种定义方法,这里给出常见的两种定义示例:

3.4.1 通过验证用户保护服务

  • 即:只由已通过身份验证的用户访问;
  1. //必须使用该注解,且需要扩展 ResourceServerConfigurerAdapter 类
  2. @Configuration
  3. public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
  4. //访问规则在 configure() 方法中定义,并且通过传入方法的 HttpSecurity 对象配置
  5. @Override
  6. public void configure(HttpSecurity http) throws Exception{
  7. http.authorizeRequests().anyRequest().authenticated();
  8. }
  9. }
  • anyRequest().authenticated() 表示需要由已通过验证的用户访问;

3.4.2 通过特定角色保护服务

  • 限制只有 ADMIN 用户才能调用该服务的 DELETE 方法;
  1. @Configuration
  2. public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
  3. @Override
  4. public void configure(HttpSecurity http) throws Exception{
  5. http
  6. .authorizeRequests()
  7. .antMatchers(HttpMethod.DELETE, "/v1/xxxservices/**") //运行部开发人员限制对受保护的 URL 和 HTTP DELETE 动词的调用
  8. .hasRole("ADMIN") //允许访问的角色列表
  9. .anyRequest()
  10. .authenticated();
  11. }
  12. }
  • anyRequest().authenticated() 表示仍需要由已通过验证的用户访问;
  • 结合本篇《2.5 为应用程序定义用户 ID、密码和角色》的示例,这里使用 john.carnell USER 用户访问资源将被拒绝,而使用 william.woodward ADMIN 用户访问资源将被通过;

4. 在上下游服务中传播 OAuth2 访问令牌

  • 用户已经向 OAuth2 服务器进行了验证,调用 EagleEye Web 客户端;
  • EagleEye Web 应用程序( OAuth2 服务器)将通过 HTTP 首都 Authorization 添加 OAuth2 访问令牌;
  • Zuul 将查找许可证服务端点,然后将调用转发到其中一个许可证服务的服务器;
  • 服务网关需要从传入的调用中复制 HTTP 首部 Authorization
  • 受保护服务使用 OAuth2 服务器确认令牌;

4.1 配置服务网关的黑名单

在 Zuul 的 application.yml 的配置文件里;

  • 因为在整个验证流程中,我们需要将 HTTP 首部 Authorization 传递上下游进行权限认证;

  • 但在默认情况下,Zuul 不会将敏感的 HTTP 首部(如 Cookie、Set-Cokkie 和 Authorization)转发到下游服务;

  • 需要配置 Zuul 的黑名单放行 Authorization;

    1. zuul:
    2. sensitiveHeaders: Cookie , Set-Cookie
  • 上述配置表示拦截 Cookie , Set-Cookie 传递下游,而 Authorization 会放行;

4.2 修改上游服务业务代码

  • 业务代码需要保证将 HTTP 首部 Authorization 注入服务的上下游;

4.2.1 下游服务

  • 这里的下游服务就是受保护的服务;
  • 其构建方法同本篇的《3. 使用 OAuth2 建立并保护服务资源》

4.2.2 在上游服务中公开 OAuth2RestTemplate 类

可以在主程序类上,也可以在主程序所在包及其子包里创建类;

  • 使该类可以被自动装配到调用另一个受 OAuth2 保护的服务;

    1. @Bean
    2. public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext, OAuth2ProtectedResourceDetails details) {
    3. return new OAuth2RestTemplate(details, oauth2ClientContext);
    4. }

4.2.3 在上游服务中用 OAuth2RestTemplate 来传播 OAuth2 访问令牌

  • 自动装配 OAuth2RestTemplate;
  1. @Component
  2. public class OrganizationRestTemplateClient {
  3. //OAuth2RestTemplate 是标准的 RestTemplate 的增强式替代品,可处理 OAuth2 访问令牌
  4. @Autowired
  5. OAuth2RestTemplate restTemplate;
  6. public Organization getOrganization(String organizationId){
  7. //调用组织服务的方式与标准的 RestTemplate 完全相同
  8. ResponseEntity<Organization> restExchange =
  9. restTemplate.exchange(
  10. "http://zuulserver:5555/api/organization/v1/organizations/{organizationId}",
  11. HttpMethod.GET,
  12. null, Organization.class, organizationId);
  13. return restExchange.getBody();
  14. }
  15. }

最后

新人制作,如有错误,欢迎指出,感激不尽!
欢迎关注公众号,会分享一些更日常的东西!
如需转载,请标注出处!

微服务架构 | 7.1 基于 OAuth2 的安全认证的更多相关文章

  1. 庐山真面目之十微服务架构 Net Core 基于 Docker 容器部署 Nginx 集群

    庐山真面目之十微服务架构 Net Core 基于 Docker 容器部署 Nginx 集群 一.简介      前面的两篇文章,我们已经介绍了Net Core项目基于Docker容器部署在Linux服 ...

  2. 微服务架构 | 4.2 基于 Feign 与 OpenFeign 的服务接口调用

    目录 前言 1. OpenFeign 基本知识 1.1 Feign 是什么 1.2 Feign 的出现解决了什么问题 1.3 Feign 与 OpenFeign 的区别与对比 2. 在服务消费者端开启 ...

  3. JHipster生成微服务架构的应用栈(二)- 认证微服务示例

    本系列文章演示如何用JHipster生成一个微服务架构风格的应用栈. 环境需求:安装好JHipster开发环境的CentOS 7.4(参考这里) 应用栈名称:appstack 认证微服务: uaa 业 ...

  4. 微服务架构 | 5.2 基于 Sentinel 的服务限流及熔断

    目录 前言 1. Sentinel 基础知识 1.1 Sentinel 的特性 1.2 Sentinel 的组成 1.3 Sentinel 控制台上的 9 个功能 1.4 Sentinel 工作原理 ...

  5. 基于 Docker 的微服务架构实践

    本文来自作者 未闻 在 GitChat 分享的{基于 Docker 的微服务架构实践} 前言 基于 Docker 的容器技术是在2015年的时候开始接触的,两年多的时间,作为一名 Docker 的 D ...

  6. 几种常见的微服务架构方案简述——ZeroC IceGrid、Spring Cloud、基于消息队列

    微服务架构是当前很热门的一个概念,它不是凭空产生的,是技术发展的必然结果.虽然微服务架构没有公认的技术标准和规范草案,但业界已经有一些很有影响力的开源微服务架构平台,架构师可以根据公司的技术实力并结合 ...

  7. 几种常见的微服务架构方案——ZeroC IceGrid、Spring Cloud、基于消息队列、Docker Swarm

    微服务架构是当前很热门的一个概念,它不是凭空产生的,是技术发展的必然结果.虽然微服务架构没有公认的技术标准和规范草案,但业界已经有一些很有影响力的开源微服务架构平台,架构师可以根据公司的技术实力并结合 ...

  8. 微服务架构之思维三部曲:What、Why、How

    本文转自:http://www.servicemesh.cn/?/article/49 What:什么是微服务? 某百科对微服务架构的定义和阐述:微服务可以在“自己的程序”中运行,并通过“轻量级设备与 ...

  9. 庐山真面目之六微服务架构Consul集群、Ocelot网关集群和Nginx版本实现

    庐山真面目之六微服务架构Consul集群.Ocelot网关集群和Nginx版本实现 一.简介      在上一篇文章<庐山真面目之五微服务架构Consul集群.Ocelot网关和Nginx版本实 ...

随机推荐

  1. 【LeetCode】714. Best Time to Buy and Sell Stock with Transaction Fee 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 动态规划 日期 题目地址:https://leetc ...

  2. matplotlib 高阶之path tutorial

    目录 Bezier example 用path来画柱状图 随便玩玩 import matplotlib.pyplot as plt from matplotlib.path import Path i ...

  3. Java初学者作业——使用eclipse简单的输出(打印)游戏菜单

    返回本章节 返回作业目录 需求说明: 使用eclipse创建Java项目,在Java程序中输出游戏菜单. 为 Java 程序添加单行和多行注释. 实现思路: 在 eclipse 中创建项目及Java类 ...

  4. vue3+TypeScript+vue-router使用

    简单使用 创建项目 vue-cli创建 $npm install -g @vue/cli $vue --version @vue/cli 4.5.15 $vue create my-project 然 ...

  5. 前端必备,5大mock省时提效小tips,用了提前下班一小时

    ​ 一.一些为难前端的业务场景 在我的工作经历里,需要等待后端童鞋配合我的情形大概有以下几种: a.我们跟外部有项目合作,需要调用到第三方接口. 一般这种情况下,商务那边谈合同,走流程,等第三方审核, ...

  6. python连接真机或模拟器

    前言: 最近写自动化代码的时候,使用模拟器运行 python + appium代码时,APP闪退了,只能使用真机运行代码了.真机要怎么配置设备的信息呢? 怎么配置设备的信息? 配置是使用真机还是模拟器 ...

  7. spring-Ioc(二)学习笔记

    属性注入方式 设值注入:也就是set注入,通过setter方法注入 java Bean private ITestDao dao; public void setDao(ITestDao dao){ ...

  8. nginx 配置 ^~ 的用法妙处,403阻断

    看看这个 location /css/ { alias D:/我的项目/2-代码/express/src/main/resources/static/css/ ; } 咋一看貌似没有毛病,访问 htt ...

  9. Jquery通过遍历数组给checkbox赋默认值

    需求:有一个数组:(北京菜,粤菜),checkbox如下: 现在想通过遍历这个数组,使数组里包含的值,在checkbox选中 代码: var flavors = new Array([北京菜 , 粤菜 ...

  10. C语言 运算符优先级和结合方向

    运算符优先级和结合方向 初级运算符( ).[ ].->..  高于  单目运算符  高于  算数运算符(先乘除后加减)  高于  关系运算符  高于  逻辑运算符(不包括!)  高于  条件运算 ...