SpringCloud | 三、Spring Security OAuth2+JWT授权服务[Finchley版]

源码:https://github.com/GiraffePeng/spring-cloud-scaffolding

授权服务器

  • 客户端信息以及用户信息按照生产环境模拟,将其保存在了数据库中。
  • 自定义了Token增强器,在其负荷部分加入自定义内容
  • 使用JWT来实现token的生成
  • 自定义根据手机号验证码和手机号密码的形式授权的grant_type,可基本满足于移动端的开发

1、oauth2介绍

OAuth2.0是一套授权体系的开放标准,定义了四大角色:

  • 资源拥有者,也就是用户,由用于授予三方应用权限
  • 客户端,也就是三方应用程序,在访问用户资源之前需要用户授权
  • 资源提供者,或者说资源服务器,提供资源,需要实现Token和ClientID的校验,以及做好相应的权限控制
  • 授权服务器,验证用户身份,为客户端颁发Token,并且维护管理ClientID、Token以及用户

其中后三项都可以是独立的程序。OAuth2.0标准同时定义了四种授权模式,这里介绍常用的三种(授权码、密码模式、客户端模式)

1、不管是哪种模式,通用流程如下:

  • 三方网站(或者说客户端)需要先向授权服务器去申请一套接入的ClientID+ClientSecret
  • 用任意一种模式拿到访问Token(流程见下)
  • 拿着访问Token去资源服务器请求资源
  • 资源服务器根据Token查询到Token对应的权限进行权限控制

2、授权码模式,最标准最安全的模式,适合和外部交互,流程是:

  • 三方网站客户端转到授权服务器,上送ClientID,授权范围Scope、重定向地址RedirectUri等信息
  • 用户在授权服务器进行登录并且进行授权批准(授权批准这步可以配置为自动完成)
  • 授权完成后重定向回到之前客户端提供的重定向地址,附上授权码
  • 三方网站服务端通过授权码+ClientID+ClientSecret去授权服务器换取Token(Token含访问Token和刷新Token,访问Token过去后用刷新Token去获得新的访问Token)
  • 你可能会问这个模式为什么这么复杂,为什么安全呢?因为我们不会对外暴露ClientSecret,不会对外暴露访问Token,使用授权码换取Token的过程是服务端进行,客户端拿到的只是一次性的授权码

3、密码凭证模式,适合内部系统之间使用的模式(客户端是自己人,客户端需要拿到用户帐号密码),流程是:

  • 用户提供帐号密码给客户端
  • 客户端凭着用户的帐号密码,以及客户端自己的ClientID+ClientSecret去授权服务器换取Token

4、客户端模式,适合内部服务端之间使用的模式:

  • 和用户没有关系,不是基于用户的授权
  • 客户端凭着自己的ClientID+ClientSecret去授权服务器换取Token

2、JWT

通过 JWT 配合 Spring Security OAuth2 使用的方式,可以避免每次请求都远程调度认证授权服务。资源服务器只需要从授权服务器 验证一次,返回 JWT。返回的 JWT 包含了 用户 的所有信息,包括 权限信息

2.1、什么是JWT

JSON Web Token(JWT)是一种开放的标准(RFC 7519),JWT 定义了一种 紧凑 且 自包含 的标准,旨在将各个主体的信息包装为 JSON 对象。主体信息 是通过 数字签名 进行 加密 和 验证 的。经常使用 HMAC 算法或 RSA(公钥/私钥 的 非对称性加密)算法对 JWT 进行签名,安全性很高。

  • 紧凑型:数据体积小,可通过 POST 请求参数 或 HTTP 请求头 发送。
  • 自包含:JWT 包含了主体的所有信息,避免了 每个请求 都需要向 Uaa 服务验证身份,降低了 服务器的负载。

2.2、JWT结构

JWT 的结构由三部分组成:Header(头)、Payload(有效负荷)和 Signature(签名)。因此 JWT 通常的格式是 xxxxx.yyyyy.zzzzz

2.2.1、Header

Header通常是由两部分组成:令牌的类型(即 JWT)和使用的 算法类型,如 HMAC、SHA256 和 RSA。例如:

{
   
    "typ": "JWT",
    "alg": "HS256"
}

将 Header 用 Base64 编码作为 JWT的第一部分,不建议在 JWT 的 Header 中放置 敏感信息。

2.2.2、Payload

第二部分 Payload 是 JWT 的 主体内容部分,它包含 声明 信息。声明是关于 用户 和 其他数据 的声明。

声明有三种类型: registered、public 和 private。

  • Registered claimsJWT 提供了一组 预定义 的声明,它们不是 强制的,但是推荐使用。JWT 指定 七个默认 字段供选择:
注册声明 字段含义
iss 发行人
exp 到期时间
sub 主题
aud 用户
nbf 在此之前不可用
iat 发布时间
jti 用于标识JWT的ID
  • Public claims:可以随意定义
  • Private claims:用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。
    下面是 Payload 部分的一个示例:
{
   
    "sub": "123456789",
    "name": "John Doe",
    "admin": true
}

将 Payload 用 Base64 编码作为 JWT 的 第二部分,不建议在 JWT 的 Payload 中放置 敏感信息。

2.2.3、Signature

要创建签名部分,需要利用 秘钥 对 Base64 编码后的 Header 和 Payload 进行 加密,加密算法的公式如下:

HMACSHA256(
    base64UrlEncode(header) + '.' +
    base64UrlEncode(payload),
    secret
)

签名可以用于验证消息在传递过程中有没有被更改。对于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方。

2.3、JWT的工作方式

客户端 获取 JWT 后,对于以后的 每次请求,都不需要再通过 授权服务 来判断该请求的 用户 以及该 用户的权限。在微服务系统中,可以利用 JWT 实现 单点登录。认证流程图如下:

3、授权服务器的搭建

父级pom.xml这里省略,基于父级工程spring-cloud-scaffolding即可。

3.1、授权服务器引入依赖:

<dependencies>
    <!-- oauth2.0依赖 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
    <!-- web相关依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--reids -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!--jpa数据访问 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!--mysql连接 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!-- 断路器-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <!-- 健康监控-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- 链路跟踪-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zipkin</artifactId>
    </dependency>
    <!-- 链路跟踪-->
    <dependency>
        <groupId>com.github.gavlyukovskiy</groupId>
        <artifactId>p6spy-spring-boot-starter</artifactId>
        <version>1.4.3</version>
    </dependency>
    <!-- 注册中心注册-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
</dependencies>

3.2、建立application.yml文件:

spring:
  application:
    name: auth-service
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/ceshi?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
    username: huaxin
    password: Koreyoshih527
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  redis:
    host: localhost
    database: 0
    port: 6379

hystrix:
    command:
        default:
            execution:
                isolation:
                    thread:
                        timeout-in-milliseconds: 3000

server:
  port: 8599
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8865/eureka/

会使用到mysql数据库,授权服务器的端口是8599。

3.3、建立表

因为授权服务器的客户端信息以及用户信息要放入数据库中,我们需要初始化一些表:

  • user_auth表用于oauth2的用户信息记录。
  • member_auth表用于移动端oauth2的会员信息记录。
  • role_auth表,存放了用户的权限信息
  • oauth_approvals授权批准表,存放了用户授权第三方服务器的批准情况
  • oauth_client_details,客户端信息表,存放客户端的ID、密码、权限、允许访问的资源服务器ID以及允许使用的授权模式等信息
  • oauth_code授权码表,存放了授权码。

表结构如下:

CREATE TABLE `user_auth` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `password` varchar(255) DEFAULT NULL,
  `username` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UK_sb8bbouer5wak8vyiiy4pf2bx` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表';

CREATE TABLE `member_auth` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `password` varchar(255) DEFAULT NULL,
  `username` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UK_sb8bbouer5wak8vyiiy4pf2bx` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=83 DEFAULT CHARSET=utf8 COMMENT='会员表';

CREATE TABLE `role_auth` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `authority` varchar(255) NOT NULL,
  `user_id` bigint(20) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UK_sb8bbouer5wak8vyiiy4pf2bx` (`authority`)
) ENGINE=InnoDB AUTO_INCREMENT=82 DEFAULT CHARSET=utf8 COMMENT='用户角色表';

CREATE TABLE `oauth_approvals` (
  `userId` varchar(256) DEFAULT NULL,
  `clientId` varchar(256) DEFAULT NULL,
  `partnerKey` varchar(32) DEFAULT NULL,
  `scope` varchar(256) DEFAULT NULL,
  `status` varchar(10) DEFAULT NULL,
  `expiresAt` datetime DEFAULT NULL,
  `lastModifiedAt` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `oauth_client_details` (
  `client_id` varchar(64) NOT NULL,
  `resource_ids` varchar(255) DEFAULT NULL,
  `client_secret` varchar(255) DEFAULT NULL,
  `scope` varchar(255) DEFAULT NULL,
  `authorized_grant_types` varchar(255
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值