一步一步搭建 OAuth 认证服务器

现在越来越多开放的互联网公司提供对外的 API 接口,使得第三方应用开发人员可以开发基于该平台接口的应用程序。国外有TwitterFlicker Service等;国内的,像腾讯微博开放平台新浪微博开放平台等等。

这些平台接口的认证方式,无一例外的,都采取了 OAuth 来实现(Twitter原来使用的是Basic Auth方式,后来全面转向OAuth)。

那么,OAuth 是什么?OAuth认证又有什么好处呢?

OAuth 是什么

关于OAuth的定义,在 OAuth官网 的首页上,有一行大大的文字说明:

(OAuth) 是一个开放协议,它以一种简单、标准的方式实现对桌面和 Web 的应用程序的安全 API 认证。

OAuth 1.0 协议(中文版 | 英文版)这样介绍OAuth:“OAuth 协议致力于使网站和应用程序(统称为消费方)能够在无须用户透露其认证证书的情况下,通过 API 访问某个web服务(统称为服务提供方)的受保护资源。更一般地说,OAuth 为 API 认证提供了一个可自由实现且通用的方法”。

关于 OAuth 的用途,OAuth 1.0 协议(中文版 | 英文版)上举了一个例子:某打印服务提供商 printer.example.com(消费方),希望在无须用户提供其照片存储站点密码的情况下,访问用户储存在 photos.example.net(服务提供方)上的个人照片。

假如没有 OAuth,用户必须要向消费方也就是 printer 提供自己在服务提供商 photos 上的授权资料(通常是密码),消费方利用这个授权资料,通过服务提供方的权限验证,从而获得要打印的图片。这样看似没有什么问题。但是用户的授权资料,通常在这一过程中被消费方有意或者无意地窃取或泄露,从而对用户和服务提供方的信息安全造成威胁。

而如果利用 OAuth 进行此过程的授权,用户的授权资料并不会传递给第三方(也就是消费方,通常是App应用),而消费方只需要将用户引导至服务提供方的授权页面进行授权,使得消费方获得访问受限资源的权限即可。而在此过程中,用户授权过程是在服务提供方进行的,消费方并不会直接接触到用户的授权资料,因此一般不会造成用户授权资料的泄密,从而既保证了用户和服务提供方的信息安全,又使得消费方完成了对受限资源的读取。可谓一举三得。

个人对 OAuth 授权过程的理解:服务提供方 SP 好比一个封闭院子,只有持卡人才能进入,用户 U 就是持卡人之一。而消费方 C 没有持卡,通常情况下是不能进入的。但是有一天,由于特殊原因,U 需要 C 帮忙去 SP 那里取一样东西。这个时候问题就来了:  C 没有持卡,不能进去院子,而 U 又不能把卡直接给 C (卡上面有很多个人机密信息,不方便外泄哦)。怎么办呢?

哦,对了,U 可以带着 C 去门口,告诉SP:这个人是我认识的,他需要进去帮我拿我的一样东西,请予放行。这样,U 既不用将带有个人私密信息的门卡交给 C,C 也通过验证拿到了属于 U 的东西。

有的人要问了,是不是下次 C 想要再进 SP 的拿 U 的东西的话,是不是就不用 U 的指引了呢?人类社会的情况通常是这样的。可惜,在 HTTP 的世界里,由于 HTTP 是无状态的协议,因此,SP 仍然不会认识 C。所以,每次 C 想要取东西,总是需要 U 的指引。是不是很麻烦呢?呵呵。但是为了安全,麻烦一点又有什何妨!

上面介绍了 OAuth 认证的基本思路,如果你还不理解,可以参考 OAuth认证流程图, 或者查看腾讯微博关于OAuth认证的介绍。OAuth 官方网站就有一篇文档教程《OAuth入门指南》,不过没有中文版本。有兴趣同学的也可以自己看看。

OAuth 认证授权有以下几个特点:

  • 1. 简单:不管是 OAuth 服务提供者还是应用开发者,都很容易于理解与使用;
  • 2. 安全:没有涉及到用户密钥等信息,更安全更灵活;
  • 3. 开放:任何服务提供商都可以实现 OAuth,任何软件开发商都可以使用 OAuth;

那么下面我们就作为服务提供商角色,来实现 OAuth 认证服务器的安装和搭建。

其实,很多先行者已经开发出了很多的 OAuth 消费方代码(客户端)和服务提供方代码(服务端),这里是它们的一些列表,其中包含了 .NET (C#/VB.NET), ColdFusion, Java, Javascript, Jifty, Objective-C, OCaml, Perl, PHP, Python, Ruby, Erlang 和其他语言的一些实现。

通过 Google,我找到了一个开源的 OAuth 服务端代码和消费方开源代码库项目——oauth-php. 我们就借此来实现。关于OAuth,项目主页的介绍是: OAuth Consumer And Server Library For PHP. 它包含一个完整实现的可扩展的OAuth存储,支持 MySQL/MySQLi,  Postgresql,  PDO 和 Oracle 等多种存储方式。

它实现了以下方法:

  • 认证进来的请求
  • 为出去的请求签名
  • 使用 body 为请求签名
  • 为多用户管理消费方的 key 和 token(服务端和消费端)
  • 记录经过类库处理的进出的请求(可以在数据库中进行可选配置)

很多网站都在使用oauth-php, 包括荷兰阿姆斯特丹Mediamatic Lab实验室出品的anyMeta CMS. 目前,oauth-php 的代码主要由Corollarium Technologies 负责维护。

为你的服务器增加 OAuth 非常简单。你需要检查进来的请求中 OAuth 认证细节。首先,我们需要四个控制器 controller 文件:

  • oauth_register.php 使消费方用户获得 key 和密钥
  • request_token.php 返回一个未认证的 request token
  • authorize.php 认证一个request token
  • access_token.php 将认证后的 request token 置换为 access token

以下的例子,假设使用的数据存储器是MySQL。你也可以使用其他数据库——当你第一次使用 OAuthStore 实例的时候指定一个参数,告诉它你要使用的数据库。

这就是假设在存储器目录,你有一个名为 OAuthStoremystore.php 文件。在这里我们使用 OAuthStoreMySQL.php 文件来实现。这也就是说,我们在实例化 OAuthStore 的时候,需要这样做:

然后,在每个请求被处理之前,都可以检查其是否带有 OAuth 认证信息:

每个消费方都使用 key 和密钥的组合和 token 和 token 密钥的组合来为它的请求进行签名。而在此之前,消费方必须要先获取属于它的消费方 key 和消费方密钥。 oauth_register.php 就是负责分发消费方 key 和密钥的控制器文件。

如果你想要更新之前注册的消费方身份,提供消费方id,key 和 secret 。key 和 secret 在更新操作完成之前不会进行改变。

你还可以请求一个特定用户注册的所有消费方列表:

request_token.php 这个控制器文件负责返回请求 token(未认证的 request token)。当消费方获取了 key 和 secret 之后,它就可以向服务器端请求未认证的 request token 了:

authorize.php 控制器文件负责认证一个用户请求 token。这个控制器负责询问用户是否允许消费方访问他的账户。如果允许,那么消费方将可以使用 request token 换取 access token。必须要保证用户在访问下面的代码之前是登录状态。OAuthServer 服务器使用 SESSION 存储一些 OAuth 状态信息,所以必须要开启seesion会话(要么是 session_start 函数,要么就是自动开启)。

access_token.php 控制器文件负责将认证的request token换成access token。access token 可以被用来为请求签名。

看完本文,如果还是一头雾水,不知道如何下手,可以继续阅读《基于PHP & MySQL 搭建 OAuth Server》。




参考文章:

1 http://www.fising.cn/2011/03/%E4%B8%80%E6%AD%A5%E4%B8%80%E6%AD%A5%E6%90%AD%E5%BB%BA-oauth-%E8%AE%A4%E8%AF%81%E6%9C%8D%E5%8A%A1%E5%99%A8.shtml
2 http://www.fising.cn/?p=581
3 http://oauth.net/documentation/getting-started/
4 http://lds2008.blogbus.com/tag/OAuth/
5 http://iamcaihuafeng.blog.sohu.com/154447409.html
6 http://www.cnblogs.com/youxilua/archive/2011/12/29/2306790.html
7 http://www.fising.cn/2011/06/%E5%9F%BA%E4%BA%8Ephp%E7%9A%84oauth%E8%AE%A4%E8%AF%81%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E6%90%AD%E5%BB%BA.shtml

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为了搭建OAuth2服务器,您可以使用现有的OAuth2服务器实现,例如Authlib或OAuthlib。以下是使用Authlib搭建OAuth2服务器的步骤: 1.安装Authlib ```shell pip install authlib ``` 2.创建一个Flask应用程序 ```python from flask import Flask app = Flask(__name__) ``` 3.配置应用程序 ```python from authlib.integrations.flask_oauth2 import AuthorizationServer from authlib.integrations.sqla_oauth2 import create_query_client_func from flask_sqlalchemy import SQLAlchemy app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///oauth2.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SECRET_KEY'] = 'secret-key' app.config['OAUTH2_REFRESH_TOKEN_GENERATOR'] = True db = SQLAlchemy(app) class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(40), unique=True) password = db.Column(db.String(100)) class Client(db.Model): id = db.Column(db.Integer, primary_key=True) client_id = db.Column(db.String(40), unique=True) client_secret = db.Column(db.String(55), nullable=False) client_name = db.Column(db.String(100), nullable=False) is_confidential = db.Column(db.Boolean) def get_client_id(self): return self.client_id def get_client_secret(self): return self.client_secret def check_redirect_uri(self, redirect_uri): return True def has_user_consent(self, user, scope): return True def get_default_redirect_uri(self): return 'http://localhost:5000/authorized' class OAuth2Client(db.Model): id = db.Column(db.Integer, primary_key=True) client_id = db.Column(db.String(40), db.ForeignKey('client.client_id')) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) user = db.relationship(User) client = db.relationship(Client) token_type = db.Column(db.String(40)) access_token = db.Column(db.String(255)) refresh_token = db.Column(db.String(255)) expires_at = db.Column(db.DateTime) query_client = create_query_client_func(db.session, Client) authorization = AuthorizationServer(app, query_client=query_client) ``` 4.实现授权和令牌端点 ```python from flask import jsonify, request from authlib.oauth2 import OAuth2Error from authlib.oauth2.rfc6749 import grants @app.route('/oauth/token', methods=['POST']) def issue_token(): grant_type = request.form.get('grant_type') if grant_type == 'password': return grants.ResourceOwnerPasswordCredentialsGrant().create_token_response() elif grant_type == 'client_credentials': return grants.ClientCredentialsGrant().create_token_response() elif grant_type == 'refresh_token': return grants.RefreshTokenGrant().create_token_response() raise OAuth2Error('unsupported_grant_type') @app.route('/oauth/authorize', methods=['GET', 'POST']) def authorize(): if request.method == 'GET': try: grant = authorization.validate_consent_request(end_user=User.query.get(1)) return grant.prompt except OAuth2Error as error: return jsonify(dict(error.get_body())) if request.method == 'POST': if request.form['confirm']: grant_user = User.query.get(1) return authorization.create_authorization_response(grant_user=grant_user) return jsonify({'error': 'User denied authorization'}) ``` 5.运行应用程序 ```shell export FLASK_APP=app.py flask run ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值