Laravel Oauth2认证 - Passport

Laravel Passport 基于thephpleague/oauth2-server包实现了完整的OAuth2服务

安装配置

  1. composer require laravel/passport
  2. 注册Laravel\Passport\PassportServiceProvider::class
  3. 迁移passport的表
	php artisan vendor:publish --tag=passport-migrations
	php artisan migrate
  1. 生成Token秘钥php artisan passport:keys
  2. User模型增加use Laravel\Passport\HasApiTokens
  3. api guarddriver切换为passport
  4. 注册OAuth2路由
	# AuthServiceProvider::boot中注册令牌签发撤销相关的路由
		- /oauth/authorize
		- /oauth/token
	Passport::routes()
  1. 设定Token过期时间(默认不过期)
	# AuthServiceProvider::boot中设置过期时间
	Passport::tokensExpireIn(Carbon::now()->addDays(15))
	Passport::refreshTokensExpireIn(Carbon::now()->addDays(30))

前端骨架

Passport提供了一套方便的JsonAPI式的Vue组件用于OAuth2相关服务

# 发布vue组件至resources/assets/js/components
php artisan vendor:publish --tag=passport-components

# resources/assets/js/app.js中注册组件
- passport-clients -> Clients.vue
- passport-authorized-clients -> AuthorizedClients.vue
- passport-personal-access-tokens -> PersonalAccessTokens.vue

# 编译asset前端程序
npm run dev

# 使用组件服务
<passport-clients></passport-clients>
<passport-authorized-clients></passport-authorized-clients>
<passport-personal-access-tokens></passport-personal-access-tokens>

Client管理

1. Artisan方式管理

# 授权服务器上注册客户端应用
php artisan passport:client
	- 提供:应用名、授权后回调地址等
	- 响应:client ID、secret

2. JsonAPI方式管理

# 认证用户名下的客户端列表
GET /oauth/clients

# 为认证用户新建客户端
POST /oauth/clients
	- 数据:{name:'', redirect:''}
	- 响应:client ID、client secret

# 为认证用户更新客户端
PUT /oauth/clients/{client-id}
	- 请求:{name:'', redirect:''}
	- 响应:client ID、client secret

# 删除认证用户名下的客户端
DELETE /oauth/clients/{client-id}

授权模式

OAuth2 设计了四种授权模式用于不同的应用场景 授权模式选择

1. 授权码 - 授权模式

  • 通过授权码的跳转工作流来签发Token
  • 广泛用于用户对第三方应用的引导授权
# 客户端应用引导用户至授权服务器
客户端:Get /redirect
授权服务器:Get /oauth/authorize?client_id=&redirect_uri=http://example.com/callback&response_type=code&scope=

# 用户同意授权后回跳至redirect_uri
- redirect_uri必须和client注册时的redirect一致
- 定制授权页面
	- php artisan vendor:publish --tag=passport-views
	- 视图路径 resources/views/vendor/passport

# 授权码交换token
- 客户端:Get /callback(即redirect_uri)
- 授权服务器:Post /oauth/token
	- 数据:{grant_type:'authorization_code', client_id:'', client_secret:'', redirect_uri:'http://example.com/callback', code:''}
	- 响应:access_token、refresh_token、expires_in

# 刷新Token
授权服务器: Post /oauth/token
	- 数据:{grant_type:'refresh_token', refresh_token:'', client_id:'', client_secret:'', scope:''}
	- 响应:access_token、refresh_token、expires_in

2. 密码 - 授权模式

  • 不用像第三方客户端应用那样通过授权码跳转
  • 方便于用户(数据属主)直接通过账号密码来交换Token

创建密码授权客户端

php artisan passport:client --password

请求Token

授权服务器:Post /oauth/token
	- 数据:{grant_type:'password', client_id:'', client_secret:'', username:'', password: '', scope: ''} #密码授权模式可以指定scope为*全部权限域
	- 响应:access_token、refresh_token、expires_in

3. 简化 - 授权模式

  • 类似于授权码授权模式,但是简化掉了授权码交换的操作
  • 常用于JS应用、手机应用等客户端凭据不能安全存储的场景
# AuthServiceProvider::boot中启用简化授权
Passport::enableImplicitGrant()

客户端:Get /redirect
授权服务器:/oauth/authorize?client_id=&redirect_uri=http://example.com/callback&response_type=token&scope=

4. 客户端凭据 - 授权模式

  • 常用于直接面向内部应用的授权(没有用户参与)
授权服务器:Post /oauth/token
	- 数据:{grant_type:'client_credentials', client_id:'', client_secret:'', scope: ''} #密码授权模式可以指定scope为*全部权限域
	- 响应:access_token、refresh_token、expires_in

私人访问Token

  • 该场景不属于OAuth2授权模式
  • 用户自我签发私人访问Token便于API测试
  • Token总是有效的, 不随过期配置而变化
# 创建私人访问客户端
php artisan passport:client --personal

# 管理私人访问Token
$scopes = []
$token = $user->createToken('token_name' [, $scopes])->accessToken

## JsonAPI管理 ##
GET /oauth/scopes  #所有权限域列表
GET /oauth/personal-access-tokens  #用户创建的所有私人访问Token
POST /oauth/personal-access-tokens {name:'token_name', scopes:[]}  #新建私人访问令牌
DELETE /oauth/personal-access-tokens/{token-id}  #删除私人访问令牌

模拟用户+Scope

# 主要用于测试,当前用户模拟为指定用户
Passport::actingAs($user, array scopes)

Artisan快捷方式

 # 快捷生成三个组件:
	 - token秘钥
	 - 密码授权客户端
	 - personal access client
php artisan passport:install

路由保护

  • ->middleware('auth:api') (配置api guard使用passport driver
  • 客户端请求需带上头部
    • Accept: application/json
    • Authorization: Bearer Token

Token Scopes

定义Scope - 代表了一个权限组

# AuthServiceProvider::boot中定义
Passport::tokensCan([
    '权限名' => '权限描述',
    ...
]);

Token设定Scope

- 授权码模式
	客户端:Get /redirect
	授权服务器:Get /oauth/authorize?client_id=&redirect_uri=http://example.com/callback&response_type=code&scope=权限列表(空格隔开)
- 私人令牌模式
	$scopes = []
	$token = $user->createToken('token_name' [, $scopes])->accessToken

检查Scope

# `\App\Http\Kernel::$routeMiddleware`注册以下路由中间件
- 'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class
	* 检查满足所有权限域
	* `->middleware('scopes:权限域1,...')`
- 'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class
	* 检查满足任一权限域
	* `->middleware('scope:权限域1,...')`

# 直接检查用户Token是否拥有指定权限域
$user->tokenCan('权限域')

JS访问API

  • web中间件增加\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class
    • 自动在响应上设置一个laravel_token cookie(内含一个加密的JWT
    • 不用再显式配置HTPP头部的Token
  • 根据JS框架实际情况配置请求自动带上头部
    • X-CSRF-TOKENCSRF Token
    • X-Requested-WithXMLHttpRequest

Token签发事件

  • Laravel\Passport\Events\AccessTokenCreated
  • Laravel\Passport\Events\RefreshTokenCreated

注意点

  • 凭据Credential和令牌Token的区别理解(分别是独立存储,Credential处理client_id/secret)

猜你喜欢

转载自my.oschina.net/u/2400083/blog/1036519