Spring Cloud微服务安全实战_4-4_OAuth2协议与微服务安全
接上篇文章,在这个流程中,PostMan可以代表客户端应用,订单服务是资源服务器,唯一缺少的是 认证服务器 ,下面来搭建认证服务器
项目结构:
Pom.xml : DependencyManager 引入SpringCloud的配置,Dependency引入 spring-cloud-starter-oauth2
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.nb.security</groupId>
- <artifactId>nb-server-auth</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <properties>
- <mybatis-plus.version>3.1.2</mybatis-plus.version>
- <java.version>1.8</java.version>
- <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <mybatis-plus.version>3.1.2</mybatis-plus.version>
- <druid.version>1.1.17</druid.version>
- <jwt.version>0.9.1</jwt.version>
- <commons.version>2.6</commons.version>
- <aliyun-java-sdk-core.version>3.2.3</aliyun-java-sdk-core.version>
- <aliyun-java-sdk-dysmsapi.version>1.0.0</aliyun-java-sdk-dysmsapi.version>
- <aliyun.oss.version>3.6.0</aliyun.oss.version>
- <qc.cos.version>5.6.5</qc.cos.version>
- </properties>
- <dependencyManagement>
- <dependencies>
- <dependency>
- <!-- Import dependency management from Spring Boot -->
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-dependencies</artifactId>
- <version>2.1.6.RELEASE</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <!--spring cloud-->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-dependencies</artifactId>
- <version>Greenwich.SR2</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-oauth2</artifactId>
- </dependency>
- <!--集成mybatisplus-->
- <!-- mybatis-plus -->
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-boot-starter</artifactId>
- <version>${mybatis-plus.version}</version>
- </dependency>
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-generator</artifactId>
- <version>3.2.0</version>
- </dependency>
- <dependency>
- <groupId>org.apache.velocity</groupId>
- <artifactId>velocity-engine-core</artifactId>
- <version>2.1</version>
- </dependency>
- <dependency>
- <groupId>org.freemarker</groupId>
- <artifactId>freemarker</artifactId>
- <version>2.3.29</version>
- </dependency>
- <!-- druid -->
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>druid-spring-boot-starter</artifactId>
- <version>${druid.version}</version>
- </dependency>
- <!--mysql-->
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>5.1.47</version>
- <scope>runtime</scope>
- </dependency>
- <!--commons-lang3-->
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <!--指定JDK编译版本 -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>1.8</source>
- <target>1.8</target>
- <encoding>UTF-8</encoding>
- </configuration>
- </plugin>
- <!-- 打包跳过测试 -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <skipTests>true</skipTests>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
- </project>
application.yml :
- server:
- port:
- spring:
- application:
- name: auth-server
- datasource:
- url: jdbc:mysql://localhost:3306/db_oauth?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
- username: root
- password: root
- driver-class-name: com.mysql.jdbc.Driver
- #mybatis plus 设置
- mybatis-plus:
- mapper-locations: classpath*:mapper/*Mapper.xml
- global-config:
- # 关闭MP3.0自带的banner
- banner: false
- db-config:
- #主键类型 0:"数据库ID自增",1:"该类型为未设置主键类型", 2:"用户输入ID",3:"全局唯一ID (数字类型唯一ID)", 4:"全局唯一ID UUID",5:"字符串全局唯一ID (idWorker 的字符串表示)";
- id-type: 0
- # 默认数据库表下划线命名
- table-underline: true
- configuration:
- # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
- log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
下面开始新建认证服务器的配置类,写代码之前,先来看一下下图,
作为认证服务器,OAuth2 协议里的其他几个角色他都要知道,认证服务器都要知道各个角色都是谁,他们各自的特征是什么。
1,要知道有哪些客户端应用 来申请令牌
2,要知道有哪些合法的用户
3,要知道发出去的令牌,能够访问哪些资源服务器
写代码
我们需要新建一个认证服务器配置类 OAuth2AuthServerConfig ,继承 AuthorizationServerConfigurerAdapter ,AuthorizationServerConfigurerAdapter 是认证服务器适配器,我们看一下的源码:
- /**
- * @author Dave Syer
- *
- */
- public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {
- @Override
- public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
- }
- @Override
- public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
- }
- @Override
- public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
- }
- }
里面有三个方法,这三个方法,正对应上图中箭头所指的三个问题,我们需要重写这三个方法,实现自己的配置。
1,配置Client信息
从图中可以看出,认证服务器要配置两个Client,一个是【客户端应用】,他需要来认证服务器申请令牌,一个是 【订单服务】,他要来认证服务器验令牌。
重写AuthorizationServerConfigurerAdapter 的 configure(ClientDetailsServiceConfigurer clients) throws Exception 方法
- /**
- * Created by: 李浩洋 on 2019-10-29
- *
- * 认证服务器
- **/
- @Configuration //这是一个配置类
- @EnableAuthorizationServer //当前应用是一个认证服务器
- public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {//AuthorizationServerConfigurerAdapter:认证服务器适配器
- //Spring 对密码加密的封装,自己配置下
- @Autowired
- private PasswordEncoder passwordEncoder;
- /**
- * 配置客户端应用的信息,让认证服务器知道有哪些客户端应用来申请令牌。
- *
- * ClientDetailsServiceConfigurer:客户端的详情服务的配置
- * @param clients
- * @throws Exception
- */
- @Override
- public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
- clients.inMemory()//配置在内存里,后面修改为数据库里
- //~============== 注册【客户端应用】,使客户端应用能够访问认证服务器 ===========
- .withClient("orderApp")
- .secret(passwordEncoder.encode("123456")) //spring
- .scopes("read","write") //orderApp有哪些权限
- .accessTokenValiditySeconds(3600) //token的有效期
- .resourceIds("order-server") //资源服务器的id。发给orderApp的token,能访问哪些资源服务器,可以多个
- .authorizedGrantTypes("password")//授权方式,再给orderApp做授权的时候可以用哪种授权方式授权
- //~=============客户端应用配置结束 =====================
- .and()
- //~============== 注册【资源服务器-订单服务】(因为订单服务需要来认证服务器验令牌),使订单服务也能够访问认证服务器 ===========
- .withClient("orderServer")
- .secret(passwordEncoder.encode("123456")) //spring
- .scopes("read","write") //有哪些权限
- .accessTokenValiditySeconds(3600) //token的有效期
- .resourceIds("order-server") //资源服务器的id
- .authorizedGrantTypes("password");//授权方式
- }
- }
2,配置用户 信息
告诉认证服务器,有哪些用户可以来访问认证服务器
重写AuthorizationServerConfigurerAdapter 的 configure(AuthorizationServerEndpointsConfigurer endpoints) 方法
- /**
- * Created by: 李浩洋 on 2019-10-29
- *
- * 认证服务器
- **/
- @Configuration //这是一个配置类
- @EnableAuthorizationServer //当前应用是一个认证服务器
- public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {//AuthorizationServerConfigurerAdapter:认证服务器适配器
- //Spring 对密码加密的封装,自己配置下
- @Autowired
- private PasswordEncoder passwordEncoder;
- @Autowired
- private AuthenticationManager authenticationManager;
- /**
- * 1,配置客户端应用的信息,让认证服务器知道有哪些客户端应用来申请令牌。
- *
- * ClientDetailsServiceConfigurer:客户端的详情服务的配置
- * @param clients
- * @throws Exception
- */
- @Override
- public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
- clients.inMemory()//配置在内存里,后面修改为数据库里
- //~============== 注册【客户端应用】,使客户端应用能够访问认证服务器 ===========
- .withClient("orderApp")
- .secret(passwordEncoder.encode("123456")) //spring
- .scopes("read","write") //orderApp有哪些权限
- .accessTokenValiditySeconds(3600) //token的有效期
- .resourceIds("order-server") //资源服务器的id。发给orderApp的token,能访问哪些资源服务器,可以多个
- .authorizedGrantTypes("password")//授权方式,再给orderApp做授权的时候可以用哪种授权方式授权
- //~=============客户端应用配置结束 =====================
- .and()
- //~============== 注册【资源服务器-订单服务】(因为订单服务需要来认证服务器验令牌),使订单服务也能够访问认证服务器 ===========
- .withClient("orderServer")
- .secret(passwordEncoder.encode("123456")) //spring
- .scopes("read","write") //orderServer有哪些权限
- .accessTokenValiditySeconds(3600) //token的有效期
- .resourceIds("order-server") //资源服务器的id。
- .authorizedGrantTypes("password");//授权方式,
- }
- /**
- *,2,配置用户信息
- * @param endpoints
- * @throws Exception
- */
- @Override
- public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
- //传给他一个authenticationManager用来校验传过来的用户信息是不是合法的,注进来一个,自己实现
- endpoints.authenticationManager(authenticationManager);
- }
- /**
- * 3,配置资源服务器过来验token 的规则
- * @param security
- * @throws Exception
- */
- @Override
- public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
- /**
- * 过来验令牌有效性的请求,不是谁都能验的,必须要是经过身份认证的。
- * 所谓身份认证就是,必须携带clientId,clientSecret,否则随便一请求过来验token是不验的
- */
- security.checkTokenAccess("isAuthenticated()");
- }
- }
上边的 加密解密类 PasswordEncoder 和 配置用户信息的 AuthenticationManager 还没有地方来,下边配置这俩类。
新建配置类 OAuth2WebSecurityConfig 继承 WebSecurityConfigurerAdapter
- package com.nb.security.server.auth;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Lazy;
- import org.springframework.security.authentication.AuthenticationManager;
- import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
- import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
- import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
- import org.springframework.security.core.userdetails.UserDetailsService;
- import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
- import org.springframework.security.crypto.password.PasswordEncoder;
- /**
- * Created by: 李浩洋 on 2019-10-29
- **/
- @Configuration
- @EnableWebSecurity //使安全配置生效
- public class OAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter {
- @Autowired
- private UserDetailsService userDetailsService;
- @Autowired
- private PasswordEncoder passwordEncoder;
- /**
- * AuthenticationManagerBuilder 是用来构建 AuthenticationManager(处理登录操作)的
- * 需要两个东西:userDetailsService 、passwordEncoder
- * @param auth
- * @throws Exception
- */
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth.userDetailsService(userDetailsService) //获取用户信息
- .passwordEncoder(passwordEncoder); //比对密码
- }
- /**
- * 把AuthenticationManager暴露为bean
- * @return
- * @throws Exception
- */
- @Bean
- @Override
- public AuthenticationManager authenticationManagerBean() throws Exception {
- return super.authenticationManagerBean();
- }
- }
+++++++++++++++ 备注 ++++++++++++++++++++++++++
2019-12-15-23:00, passwordEncoder 本应该配置在上述类里,但是配置后报错循环依赖,暂时将其写在启动类里,不报错
+++++++++++++++++++++++++++++++++++++++++++++
UserDetailsService接口
UserDetailsService接口,只有一个方法,返回UserDetails 接口:
- public interface UserDetailsService {
- UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
- }
- loadUserByUsername,这里不用比对密码,比对密码是在AuthenticationManager里做的。
- UserDetails接口如下,提供了一些见名知意的方法,我们需要自定义自己的UserDetails实现类,比如你的User类实现这个接口 :
- public interface UserDetails extends Serializable {
- // ~ Methods
- // ========================================================================================================
- Collection<? extends GrantedAuthority> getAuthorities();
- String getPassword();
- String getUsername();
- boolean isAccountNonExpired();
- boolean isAccountNonLocked();
- boolean isCredentialsNonExpired();
- boolean isEnabled();
- }
自定义UserDetailsService 实现类:
- /**
- * Created by: 李浩洋 on 2019-10-29
- **/
- @Component//TODO:这里不写 ("userDetailsService")
- public class UserDetailsServiceImpl implements UserDetailsService {
- /**
- *
- */
- @Autowired
- private PasswordEncoder passwordEncoder;
- public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
- return User.withUsername(username)
- .password(passwordEncoder.encode("123456"))
- .authorities("ROLE_ADMIN") //权限
- .build();//构建一个User对象
- }
- }
AuthenticationManager 接口
也只有一个方法,实现类一般不需要自己实现。入参 是一个 Authentication 接口的实现,其中封装了认证的信息,不同的认证发的信息不一样,如用户名/密码 登录需要用户名密码,OAuth2则需要appId,appSecret,redirectURI等,
不同的认证方式传过来的实现不同, authenticate 方法验证完了后将其中的信息更新调,返回。
- public interface AuthenticationManager {
- Authentication authenticate(Authentication authentication)
- throws AuthenticationException;
- }
所有的准备工作都做好了,下面启动应用,来申请一个OAuth2令牌
postman请求http://localhost:9090/oauth/token ,HttpBasic传入客户端id和客户端密码。
用户名随便输,密码要和UserDetailsService一致,grant_type是说OAuth2的授权类型是授权码类型,scope是该客户端申请的权限,必须在Client配置里面包含
输入错误的密码:
输入不存在的scope:
代码github:https://github.com/lhy1234/springcloud-security/tree/chapt-4-4-andbefore
+++++++++++++分割线++++++++++++++++++++++
小结
用图片生动解释了 OAuth2 中的角色和Spring接口 AuthorizationServerConfigurerAdapter 三个方法的关系,方便记忆
实现了OAuth2的密码模式,来申请token
存在问题:passwordEncoder 如果放在了 OAuth2WebSecurityConfig配置类里面,就会报循环依赖错误,有待解决
Spring Cloud微服务安全实战_4-4_OAuth2协议与微服务安全的更多相关文章
- Spring cloud微服务安全实战-4-4 OAuth2协议与微服务安全
Oauth2 解决了cookie和session的问题 搭建认证服务器 把依赖都复制进来 因为搭建的是Oauth的服务器,所以还需要导入oauth2 开始写代码 首先创建启动类 增加配置文件 端口设置 ...
- spring cloud: zuul(二): zuul的serviceId/service-id配置(微网关)
spring cloud: zuul(二): zuul的serviceId/service-id配置(微网关) zuul: routes: #路由配置表示 myroute1: #路由名一 path: ...
- 新书上线:《Spring Boot+Spring Cloud+Vue+Element项目实战:手把手教你开发权限管理系统》,欢迎大家买回去垫椅子垫桌脚
新书上线 大家好,笔者的新书<Spring Boot+Spring Cloud+Vue+Element项目实战:手把手教你开发权限管理系统>已上线,此书内容充实.材质优良,乃家中必备垫桌脚 ...
- Spring Cloud Gateway限流实战
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- Spring Cloud架构教程 (七)消息驱动的微服务(核心概念)【Dalston版】
下图是官方文档中对于Spring Cloud Stream应用模型的结构图.从中我们可以看到,Spring Cloud Stream构建的应用程序与消息中间件之间是通过绑定器Binder相关联的,绑定 ...
- Spring Cloud架构教程 (六)消息驱动的微服务【Dalston版】
Spring Cloud Stream是一个用来为微服务应用构建消息驱动能力的框架.它可以基于Spring Boot来创建独立的.可用于生产的Spring应用程序.它通过使用Spring Integr ...
- Spring Cloud架构教程 (八)消息驱动的微服务(消费组)【Dalston版】
使用消费组实现消息消费的负载均衡 通常在生产环境,我们的每个服务都不会以单节点的方式运行在生产环境,当同一个服务启动多个实例的时候,这些实例都会绑定到同一个消息通道的目标主题(Topic)上. 默认情 ...
- Spring Cloud Sleuth超详细实战
为什么需要Spring Cloud Sleuth 微服务架构是一个分布式架构,它按业务划分服务单元,一个分布式系统往往有很多个服务单元.由于服务单元数量众多,业务的复杂性,如果出现了错误和异常,很难去 ...
- spring cloud 入门系列四:使用Hystrix 实现断路器进行服务容错保护
在微服务中,我们将系统拆分为很多个服务单元,各单元之间通过服务注册和订阅消费的方式进行相互依赖.但是如果有一些服务出现问题了会怎么样? 比如说有三个服务(ABC),A调用B,B调用C.由于网络延迟或C ...
- spring cloud 入门系列五:使用Feign 实现声明式服务调用
一.Spring Cloud Feign概念引入通过前面的随笔,我们了解如何通过Spring Cloud ribbon进行负责均衡,如何通过Spring Cloud Hystrix进行服务断路保护,两 ...
随机推荐
- Paper | Spatially Adaptive Computation Time for Residual Networks
目录 摘要 故事 SACT机制 ACT机制 SACT机制 实验 发表在2017年CVPR. 摘要 在图像检测任务中,对于图像不同的区域,我们可以分配不同层数的网络予以处理. 本文就提出了一个基于Res ...
- HTTP和RPC是现代微服务架构,HTTP和RPC是现代微服务架构
.NET Core使用gRPC打造服务间通信基础设施 一.什么是RPC rpc(远程过程调用)是一个古老而新颖的名词,他几乎与http协议同时或更早诞生,也是互联网数据传输过程中非常重要的传输机制 ...
- NXP官方的i.mx6ul板级uboot源码适配
1.前言 CoM-P6UL是盈鹏飞科技有限公司基于NXP原厂I.MX6UL芯片生产研发的核心板,本文将对CoM-P6UL适配NXP的基于Linux4.1.15版本的uboot板级源码. 2.开发环境 ...
- MongoDB副本集--Secondary节点实例恢复
场景描述 MongoDB副本集中有一台Secondary节点出现RECOVERING的状态 状态如下: arps:RECOVERING> rs.status() { "set" ...
- mysql8 安装
准备工作: 首先安装这些依赖 yum install -y flex yum install gcc gcc-c++ cmake ncurses ncurses-devel bison libaio ...
- MySQL使用crontab定时备份不执行问题
在使用crontab定时备份数据库时,发现并没有执行备份命令. 下面是定时备份的代码: 30 1 * * * /usr/local/mysql/bin/mysqldump --defaults-ext ...
- 一个比 AutoMapper 更快的模型映射的组件 Mapster
下面是官方的性能测试 Demo,感性的也可以去 Github 上下载. 贴出代码目的是如果后期直接从自己的博客中在线看. using System; using System.Collections. ...
- 【机器学习笔记】ID3构建决策树
好多算法之类的,看理论描述,让人似懂非懂,代码走一走,现象就了然了. 引: from sklearn import tree names = ['size', 'scale', 'fruit', 'b ...
- 使用WebApi和Asp.Net Core Identity 认证 Blazor WebAssembly(Blazor客户端应用)
原文:https://chrissainty.com/securing-your-blazor-apps-authentication-with-clientside-blazor-using-web ...
- MAC bash和zsh切换
bash和zsh切换 切换到bash chsh -s /bin/bash 切换到zsh chsh -s /bin/zsh 记得输入切换命令后,要重新打开终端terminal才生效哦!大功告成!