zoukankan      html  css  js  c++  java
  • 个人技术博客(α)

    这次团队开发,我们采用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相关知识

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

    我相信 坚持的力量
  • 相关阅读:
    使用beanUtils操纵javabean
    反射
    JDK5.0新特性(静态导入、自动装箱/拆箱、增强for循环、可变参数、枚举、泛形)
    Junit测试框架
    Eclipse常用快捷键
    Linux最全基础指令
    log file sync等待事件
    数据库要不要部署在docker容器内?
    MySQL启动报错-The server quit without updating PID file[FAILED]mysql/mysql.pid).
    MySQL数据库启动异常-[ERROR] [MY-011971]
  • 原文地址:https://www.cnblogs.com/hish/p/7812184.html
Copyright © 2011-2022 走看看