作者:Tony


基本的用户登录方案

传统Web网站使用Cookie+Session保持用户的登录状态,浏览器都有cookie,每次请求会带回sessionid,这样应用服务器就找出对应的session。业务逻辑里只要看session里有没有用户信息。

那么在App后台怎么实现类似的功能呢?在App后台怎么避免每次验证用户身份都需要传输用户名和密码呢?

数据在网络的传输过程中,避免数据泄漏,最基本的方案是所有涉及安全性的API请求都必须使用HTTPS协议。这个方案这边就不细谈了,

读者看看支付宝涉及登录和支付的页面,URL都是以HTTPS开头,这就意味通信是使用HTTPS。国内主流开放平台的API,例如新浪微博、腾讯等,API请求都是以HTTPS开头。

blob.png

APP授权常用的方式有:

1、OAuth:

OAuth(开放授权)是一个开放的授权标准,允许用户让第三方应用访问该用户在某一web服务上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。

OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的第三方系统(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容

下面是OAuth2.0的流程:

blob.png

这种基于OAuth的认证机制适用于个人消费者类的互联网产品,如社交类APP等应用,但是不太适合拥有自有认证权限管理的企业应用;

常用的方式可以基于Spring-auth2框架来实现;


2.基于Token方式:


类似与 JSON Web Token(JWT) Token认证机制的实现;

我们LegendShop就是基于这种方式实现App认证授权;

APP登录的时候发送加密的用户名和密码到服务器,服务器验证用户名和密码,如果成功,以某种方式比如加密随机串作为token,存储到服务器中,并返回token到APP,以后APP请求时,凡是需要验证的地方(比如用户中心的链接)都要带上该token,然后服务器端验证token,成功返回所需要的结果,失败返回错误信息,让他重新登录。其中服务器上token设置一个有效期,每次APP请求的时候都验证token和有效期。(Token信息可以存放在memcached、redis)。

如下图:

blob.png

登录过程:

实现思路:

• 第一次认证:第一次登录,用户从浏览器输入用户名/密码,提交后到服务器的登录处理的Action层(Login Action);

• Login Action调用认证服务进行用户名密码认证,如果认证通过,Login Action层调用用户信息服务获取用户信息(包括完整的用户信息及对应权限信息);

• 返回用户信息后,Login Action 生成Token的秘钥信息,并且存储至服务器中。

• 完成数据签名后,返回Json信息,完成登录过程;

1、服务端app token 授权表结构:

blob.png

securiyCode:随机生成安全码;就是所谓的钥匙。

accessToken:随机生成;

2、用户登录成功。存储服务器中,返回Token串信息:

第一次登录存储在数据库或者Nosql数据库中:

blob.png

成功返回给客户端结果:

{"accessToken":"0lPisdlBrN3V91FIjXNkkMvDJFF2fC","securiyCode":"sAkDReaj","userId":"132456","userName":"tony","verId":"V1.0"}

Android将这些信息存在SQLlite中.


3、 请求认证

如: APP用户访问 用户中心链接 : http://viemall.cn/u/orders

• 客户端(APP客户端或浏览器)通过GET或POST请求访问资源(页面或调用API)并在在每一次需要认证的请求中都带上完成签名的Token信息,我们是网络请求通信报文的headers     设置每 一次带过去的Token验证信息,后台基于过滤器来校验这个Token的有效性及是否过期;

获取传输的信息:

1
2
3
4
String userId = request.getHeader("userId");
String userName = request.getHeader("userName");
String accessToken = request.getHeader("accessToken");
String code = request.getHeader("code");

对Token信息进行解密和解码,进行数据校验:

1
2
3
4
5
6
StringBuffer content = new StringBuffer().append(userId).append(userName).
append(accessToken).append(time).append(verId).append(appToken.getSecuriyCode());
String validateCode = MD5Util.toMD5(content.toString());
if(validateCode.equals(code) ){
return true;
}