个人技术博客(α)

这次团队开发,我们采用PHP的CI框架来为安卓端提供后台接口,其中有个问题就是,如何实现用户认证?
所谓用户认证(Authentication),就是让用户登录,并且在接下来的一段时间内让用户访问网站时可以使用其账户,而不需要再次登录的机制。如果不使用这机制,用户的每个操作都需要进行频繁的登录。

那么应该如何进行用户认证呢?解决方案是使用token来认证,什么是token,token可以理解为临时令牌,有了这个令牌,就相当于给了用户不用账号密码来操作,而使用token来操作的权限,那么如何生成这个令牌并且进行用户认证呢?
我这边学到了2种方法:

  • 使用SESSION
  • 使用json web token
    当然应该还有其他操作,这里选择这两种方法来当作博客记录

使用SESSION

token的生成可以使用user_name+salt进行md5加密,加密是为了生成一串每个用户都不一样的token,同时也为了防止其他人伪造token,当用户登录后,便生成token,并且设置过期时间,这样可以使用户在一定时间内不需要再次登录来进行用户操作,生成token后,可以有两种方法实现用户认证。

1 在SESSION中做个映射,比如

    $token = md5($user_name.'saltsaltsalt');
    $_SESSION[$token] = $uid;

这样的操作,即时其他人伪造了token,我们只需要用用户的用户名进行加密,就可以判断是否被伪造了。一旦被伪造,如果对方不知道我们的salt,就无法生成相同的token。同时做映射可以记录该token的uid,以便进行其他操作。

2 将token存入数据库

将token存入数据库,但是这种方法会导致多余的数据库操作,我就想能不能有更好的方法来实现。从而学到了jwt。

使用json web token

首先,jwt是什么呢?,可以看看这篇博客
JSON Web Token - 在Web应用间安全地传递信息,
博客中说:

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。
一个JWT实际上就是一个字符串,它由三部分组成,头部载荷签名

头部(Header)

头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。例如

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

对其进行Base64编码,之后的字符串就成了JWT的Header。

载荷(Payload)

第二部分是包含声明的有效载荷。声明是关于实体(通常是用户)和附加元数据的声明。例如

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

还有一些是jwt标准的定义字段(非强制),如 iss (issuer), exp (expiration time), sub (subject), aud (audience), and others.

签名(Signature)

将header,payload生成的字符串用HS256算法进行加密。在加密的时候,我们提供一个密钥(secret)就可以得到我们加密后的内容,这就是我们要的token。

那么。jwt是如何实现token通信的呢?可以参考这篇博客。
八幅漫画理解使用JSON Web Token设计单点登录系统

简单来讲,即:用户登录成功后,生成json web token返回给请求端,这里的token的payload设置uidexp(过期时间),此后用户的所有操作,只要带jwt即可,请求时服务器先进行token验证,由于token的生成需要secret,如果第三方伪造token,我们只需要将请求的jwt解码,将header+payload+secret进行加密,必然会生成与请求的jwt不同的token,所以token的验证是安全的。如果设置了过期时间,我们还可以直接检测token的有效期。token验证成功后,我们就可以获取payload信息,比如uid等,进行用户的其他操作。但是有人可能会问,如果token被盗了怎么办? token可以在http的header中携带,也可以直接post传递,如果token被盗了,那么用户名/密码也完全可能被盗的,因此采用https加密通信就非常必要了。

最后,和SESSION这种方式比起来,jwt有什么优点呢?

  • 减轻服务端负担:比起使用session来保存cookie,JWT自身包含了所有信息,通过解密即可验证

Session方式存储用户id的最大弊病在于要占用大量服务器内存,对于较大型应用而言可能还要保存许多的状态。一般而言,大型应用还需要借助一些KV数据库和一系列缓存机制来实现Session的存储。而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。

  • jwt中的payload中携带的信息,非常方便服务器使用。
  • 还有是解决前端跨域问题等等

但是,使用jwt让服务器有了一些计算压力(例如加密、编码和解码)等。

这次实现认证,使用jwt的另外一个原因是,想要尝试新技术。

同时,还有一个技术想要记录,就是关于CI框架使用核心控制器类扩展,来实现用户的所有操作,都需要进行token验证。
在CI的application/core目录下,新建一个MY_Controller.php文件

class MY_Controller extends CI_Controller
{
	
	protected $sno;
	function __construct()
	{
		# code...
		parent::__construct();

		try
		{


				$jwt = $this->input->post('jwt',true);
				if($jwt == NULL)
				{

					throw new Exception('jwt is null');


				}else{
						$this->load->library('JWT');

						$key = "salt:let's encrypt";
						$objJWT = new JWT();
						$decoded = $objJWT->decode($jwt, $key, array('HS256'));

						if($decoded == false)
						{
							throw new Exception('jwt已经失效,请重新登录');
						}else
						{
							$decoded_array = (array) $decoded;
							$this->sno = $decoded_array['sno'];
							
						}
				}

		}
		catch(Exception $e)
		{
			echo_failure(1,$e->getMessage());
			// return;
			exit();
		}
	}

}

只要子类都继承这个父类,就可以实现进行子类操作都需要进行token的验证。

最后,想要说说这几天冲刺阶段的感受。两个字,纠结。
非常纠结。 纠结原因在于使用不熟悉的实现方式,会不会让整个团队项目后端进度更加难以跟进?
纠结了几点:

  • 我要不要使用jwt,还是直接使用之前熟悉的将token存入数据库?
  • 在腾讯云备案的域名无法定向到阿里云的服务器,要不要重新备案?不备案的话接口就只能直接用ip访问了。备案的话又太麻烦...
  • 要不要学习使用RESTful标准来设计API?使用的话又得重新学习。

结果事实是,我在Linux 就上面花了巨多的时间,因为只是学了Linux的皮毛,很多东西知道要这样做,但是就是没有自己去真正操作过,结果是遇到了各种麻烦。。比如ci框架配置nginx的路由重定向我就配了好久。以及Linux的各种bug,比如在windows上面好好的代码,在Linux上面就是跑不动....
还有就是学到了如何模拟登录教务处:

  • PHP的curl模拟HTTP请求
  • PHP简单的正则表达式使用
  • HTTP请求的header相关知识

最后纠结到此为止。
睡觉了....

posted @ 2017-11-10 01:41  tyfu  阅读(344)  评论(1编辑  收藏  举报