Cookie和Session (转)
Session和Cookie在网站开发中是用来保存用户与后端服务器的交互状态。它们有各自的缺点和优点。而且,他们的优点和应用场景是对立的。
Cookie
完整地描述:当一个用户通过HTTP访问一个服务器时,这个服务器会将一些Key/Value键值返回给客户端浏览器,并给这些数据加上一些限制条件,在条件符合时,用户下次访问这个服务器时,数据又将完整地带回给服务器。
简短版描述:Cookie就像是访问服务器时服务器颁发给用户的“身份证”,下次访问的时候带回这身份证,服务器就能识别信息。
版本:当前的Cookie有两种版本,Version 0 和Version 1,它们分别对应两种设置相应头的标识,分别是“Set-Cookie”和“Set-Cookie2”。两种版本的属性项有些不一样。值得注意的是:Java Web的Servlet并不支持Set-Cookie2响应头,在实际应用中,Set-Cookie2的一些属性项可以设置在Set-Cookie中。
Cookie是如何工作的?
真正创建Cookie是在org.apache.catalina.connector.Response类中完成的,调用generateCookieString方法将Cookie对象构造成一个字符串,构造的字符串的格式如userName="junshan";Version="1";Max-Age=1000.然后将这个字符串命名为Set-Cookie添加到MimeHeaders中。值得注意的是:Cookie对象的名字不能和Header的项重复,不然将导致错误。
我们在创建Cookie时,都创建了一个以NAME为Set-Cookie的MimeHeaders。每次调用addCookie时,都会创建一个Header。同时,在HTTP返回字符流时,是将Header中的所有项顺序地写入,而没有经过任何地修改。所以浏览器在接受HTTP返回的数据时是分别解析Header项的。
当我们请求某个URL路径时,浏览器会根据这个URL将符合条件的Cookie放在Request请求头中传回服务器,服务器通过request.getCookies()来获取所有的Cookie。
使用Cookie的注意事项
1、如果不为Cookie设置它的生存周期的话,默认是关闭浏览器的时候就销毁Cookie。
2、Cookie默认情况下是不允许出现中文字符的,如果我们要添加具有中文内容的Cookie时,我们需要使用java.net.URLEncoder先对中文进行编码,随后在进行Cookie的添加。读取Cookie时,需要使用java.net.URLDecoder对其进行解码。
3、不同的浏览器对Cookie的存储都有一些限制,通常是Cookie数量和Cookie总大小的限制。像火狐对Cookie的限制是每个域名只能有50个Cookie值,总大小不能超过4097个字。
4、Cookie在HTTP的头部,所以通常的gzip和deflate针对HTTP Body的压缩不能压缩Cookie,如果Cookie的量非常大,要做Cookie做压缩,压缩方法是将Cookie的多个K/V看作是普通的文本,做文本压缩。值得注意的是,Cookie的规范中规定,Cookie仅能保存ASCII码为34~126的可见字符,所以压缩之后的结果再进行转码,可以进行Base32或者Base64编码。
Session
完整描述:从客户端浏览器连接服务器开始,到客户端浏览器与服务器断开为止,像这样的一次会话中,Session起到的作用是跟踪用户的会话信息。
简短版描述:Session在浏览器和服务器连接过程中跟踪用户信息。
Session是如何工作的?
服务器创建HttpSession对象的前提是拥有Session ID。第一次触发通过request.getSession()方法。当当前的Seesion ID还没有相应的HttpSession对象时,服务器会创建一个新的,并将这个对象加到org.apache.catalina.Manager的session容器中保存。
Manager类将管理所有Session的生命周期,Session过期就会被回收,服务器关闭,Session
将被序列化到磁盘等。
只要这个HttpSession对象存在,用户就可以根据Session ID来获取这个对象,也就做到了对状态的保持。
从Request中获得的Session对象保存在org.apache.catalina.Manager类中,它的实现类是StandardManager,通过requestedSessionId从StandardManager的session集合中取出SandardSession对象。一个客户端对应一个StandardSession对象,这个对象正是保存着我们创建的Session值。
当Servelt容器重启或者关闭的时候,StandardManager负责持久化没有过期的StandardSession对象,它将所有的StandardSession对象持久化到“SESSIONS.ser”为文件名的文件中,到Servlet重启的时候,它会重新读取这个文件,解析出所有的Session对象,并重新保存在session集合中。
说明
session范围内的属性可以在多个页面的跳转之间共享。一旦关闭浏览器,即session结束,session范围内的属性将全部失去。
session对象是HttpSession的实例,它有两个常见的方法:getAttribute()和setAttribute()。
session的属性值可以是任何可序列化的java对象。
安全性对比
Cookie:数据保存在客户端的中,所有这些数据可以被访问到的,甚至可以人为地进行修改和添加,所以在这方面的话,Cookie是不安全的。
Session:数据保存在服务器端,只是通过Cookie传递一个SessionID而已,所以Session更适合用来存储用户私密和重要信息。如登陆密码等。
安全性解决方案:分布式Session框架
使用Cookie可以很好地解决应用的分布式部署问题,由于Cookie是将值存储在客户端的浏览器里,用户每次访问都会将最新的值带回到处理该请求的服务器,所以不存在同一个用户的请求会在同一台服务器处理而导致Cookie不一致的问题。
但是,过多的使用Cookie也会造成一些麻烦:如客户端的限制、Cookie管理混乱、安全令人担忧等。
于是,分布式Session框架诞生。
统一使用订阅服务器推送配置可以有效地集中管理资源,所以省去了每个应用都来配置Cookie,简化Cookie的管理。用统一的一个服务订阅服务器,在应用启动的时候从该服务器订阅这个应用能够使用哪些Session和Cookie项,这些配置的Session和Cookie可以限制这个应用能够使用哪些Session和Cookie,甚至控制Session和Cookie可读或者可写。这样就可以精确地控制哪些应用可以操作哪些Session和Cookie,从而有效地控制Session的安全性和Cookie的数量。
还有一个非常重要的问题是:如何处理跨域名来共享Cookie的问题。Cookie是有域名限制的,一个域名下的Cookie不能再另外一个域名下访问,如果要在两个域名之间实现登陆有效,需要实现Session同步,需要另外一个跳转应用,这个应用可以被一个或多个域名访问。 它的主要功能是从一个域名下取得sessionID,然后将这个sessionID同步到另外一个域名下。这个sessionID其实就是一个Cookie,相当于JSESSIONID,所以,经过12步的操作,一个域名不用登陆就可以取到另外一个域名下的session。
http://www.cnblogs.com/zhouyuqin/p/4693737.html
PHP 之session cookie
cookie和session有什么用? 常见的用法,比如在有些网站下载东西需要会员先登陆。http协议本身是无状态的,无法得知顾客是否已经登陆,怎么办呢?cookie和session就可以知道。再比如网上购物,用户身份认证,程序状态记录。购物车怎么知道顾客挑选过哪些商品呢?cookie和session也可以记录。总而言之,cookie和session就是能够记录顾客状态的技术,尽管二者属于不同的技术,但只要cookie能做到的,session也能做到!如果session和cookie一样安全的话,二者就没有并要同时存在了,只要cookie就好了,让客户端来分提服务器的负担,并且对于用户来说又是透明的。何乐而不为呢。
1. 什么是session?
Session的中文译名叫做“会话”,其本来的含义是指有始有终的一系列动作/消息,比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个session。
因此通过SESSION(cookie是另外一种解决办法)记录用户的有关信息,以供用户再次以此身份对web服务器提起请求时作确认。会话的发明使得一个用户在多个页面间切换时能够保存他的信息。网站编程人员都有这样的体会,每一页中的变量是不能在第三页中使用的(虽然form,url也可以实现,但这都是非常不理想的办法),而SESSION中注册的变量就可以作为全局变量使用了。SESSION的实现中采用COOKIE技术,SESSION会在客户端保存一个包含session_id(SESSION编号)的COOKIE;在服务器端保存其他session变量,比如session_name等等。当用户请求服务器时也把session_id一起发送到服务器,通过session_id提取所保存在服务器端的变量,就能识别用户是谁了。同时也不难理解为什么SESSION有时会失效了。当客户端禁用COOKIE时(点击IE中的“工具”—“Internet选项”,在弹出的对话框里点击“安全”—“自定义级别”项,将“允许每个对话COOKIE”设为禁用),session_id将无法传递,此时SESSION失效。不过PHP5在linux/unix平台可以自动检查cookie状态,如果客户端设置了禁用,则系统自动把session_id附加到url上传递。windows主机则无此功能。
a域名的网站,我登陆成功.怎样到b域名的网站,也能获得session?
在 www.a.com 上登陆后,去 www.b.com 的连接中带有 形如 http://www.b.com/xxxx.php?s=yyyyyyyyyy 的其中 yyyyyyyyyy 是在 www.a.com 中 session_id(); 的值,如果a,b在同一个服务器,直接读取文件。不在的话,b可以调用a的接口(a需要接口实现)
根据session文件获取session信息
Java代码
$sessionPath = session_save_path();
$sessionFile = $sessionPath . DS . 'sess_' . $sid;//sid是sessionid
if (file_exists($sessionFile) && (time() - filectime($sessionFile)) < $liftTime) {
$lock = new CacheLock($sid);
$lock->lock();
$content = file_get_contents($sessionFile);
$tempSession = $_SESSION;
$_SESSION = array();
session_decode($content); //成功回写$_SESSION
$result['info']['customer_id'] = $_SESSION['customer_gm']['id'];
$result['info']['customer_name'] = $_SESSION['customer_gm']['name'];
$result['info']['customer_email'] = $_SESSION['customer_gm']['email'];
$result['msg'] = '';
$result['success'] = 1;
$_SESSION = $tempSession;
$lock->unlock();
}
2. Session常见函数及用法?
● 注册SESSION变量:
PHP5使用$_SESSION[‘xxx’]=xxx注册SESSION全局变量。和GET,POST,COOKIE的使用方法相似。
Page1.php
Java代码
<?php
session_start(); //使用SESSION前必须调用该函数。
$_SESSION['passwd'] = 'mynameislikui';
$_SESSION['time'] = time();
echo '<br/><a href="page2.php">通过COOKIE传递SESSION</a>'; //如果客户端支持cookie,可通过该链接传递session到下一页。
Page2.php
Java代码
<?php
session_start();
echo $_SESSION['name']; //
echo $_SESSION['passwd']; //
echo date('Y m d H:i:s', $_SESSION['time']);
echo '<br /><a href="page1.php">返回山一页</a>';
?>
有两种方法传递一个会话 ID:
cookie cookie 更优化,但由于不总是可用
URL 参数 直接将会话 ID 嵌入到 URL 中间去。
● session_id
session_id() 用于设定或取得当前session_id。php5中既可以使用session_id(),也可以通过附加在url上的SID取得当前会话的session_id和session_name。如果session_id()有具体指定值的话,将取代当前的session_id值。使用该函数前必须启动会话:session_start();当我们使用session cookies时,如果指定了一个session_id()值,每次启动session_start()都会往客户端发送一个cookie值。不论当前session_id是否与指定值相等。session_id()如果没有指定值,则返回当前session_id();当前会话没有启动的话,则返回空字符串。
● 更改 session_id session_regenerate_id() 更改成功则返回true,失败则返回false。使用该函数可以为当前session更改session_id,但不改变当前session的其他信息。例如:
Java代码
<?php
session_start();
$old_sessionid = session_id();
session_regenerate_id();
$new_sessionid = session_id();
echo "原始 SessionID: $old_sessionid<br/>";
echo "新的 SessionID: $new_sessionid<br/>";
echo"<pre>";
print_r($_SESSION);
echo"</pre>";
?>
● session_name() 返回当前session的name或改变当前session的name。如果要改变当前session的name,必须在session_start()之前调用该函数。注意:session_name不能只由数字组成,它至少包含一个字母。否则会在每时每刻都生成一个新的session id.
session改名示例:
Java代码
<?php
$previous_name = session_name("WebsiteID");
echo "新的session名为: $previous_name<br/>";
?>
● 如何删除session?
下面是PHP官方关于删除session的案例:
Java代码
<?php
// 初始化session.
session_start();
/*** 删除所有的session变量..也可用unset($_SESSION[xxx])逐个删除。****/
$_SESSION = array();
/***删除sessin id.由于session默认是基于cookie的,所以使用setcookie删除包含session id的cookie.***/
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time() - 42000, '/');
}
// 最后彻底销毁session.
session_destroy();
?>
由此我们可以得出删除Session的步骤:
①session_start()
②$_SESSION=array()/unset($_SESSION['xxx'])
③session_destroy()
● SESSION安全:
会话模块不能保证存放在会话中的信息只能被创建该会话的用户看到。根据其存放的数据,还需要采取更多措施来主动保护会话的完整性。
评估会话中携带的数据并实施附加保护措施通常要付出代价,降低用户的方便程度。例如,如果要保护用户免于受简单的社交策略侵害(注:指在 URL 中显示的会话 ID 会被别人在电脑屏幕上看到,或被别的网站通过 HTTP Referer 得到等),则应该启用 session.use_only_cookies。此情形下,客户端必须无条件启用 cookie,否则会话就不工作。
有几种途径会将现有的会话 ID 泄露给第三方。泄露出的会话 ID 使第三方能够访问所有与指定 ID 相关联的资源。第一,URL 携带会话 ID。如果连接到外部站点,包含有会话 ID 的 URL 可能会被存在外部站点的 Referer 日志中。第二,较主动的攻击者可能会侦听网段的数据包。如果未加密,会话 ID 会以明文方式在网络中流过。对此的解决方式是在服务器上实施 SSL 并强制用户使用。
默认情况下,所有与特定会话相关的数据都被存储在由 INI 选项 session.save_path 指定的目录下的一个文件中(session文件的命名格式是:"sess_[PHPSESSID的值]"。每一个文件,里面保存了一个会话的session_encode数据)。对每个会话会建立一个文件(不论是否有数据与该会话相关)。这是由于每打开一个会话即建立一个文件,不论是否有数据写入到该文件中。注意由于和文件系统协同工作的限制,此行为有个副作用,有可能造成用户定制的会话处理器(例如用数据库)丢失了未存储数据的会话。
上面介绍函数下文将会用到,但还有一些有关session的函数也介绍一下:session_encode
函数功能:sesssion信息编码
函数原型:string session_encode(void);
返回值:字符串
功能说明:返回的字符串中包含全局变量中各变量的名称与值,形式如:a|s:12:"it is a test \";c|s:4:"lala"; a是变量名 s:12代表变量a的值"it is a test的长度是12 变量间用分号”;”分隔。session_decode
函数功能:sesssion信息解码
函数原型:boolean session_decode (string data)
返回值:布尔值
功能说明:这个函数可将session信息解码,成功则返回逻辑值true
PHP5不再使用session_id,而是把它变成一个常量SID,并保存在cookie中。如果客户端禁用了cookie,php会自动通过url自动传动传递SID,其条件是设置php.ini中的session.use_trans_sid = 1。此时即使客户端即使禁用了cookie也没关系了。
用 strip_tags() 来输出 SID 以避免 XSS 相关的攻击。
Session跨页传递问题:
session跨页传递需要考虑三种情况:
①客户端禁用了cookie。
②浏览器出现问题,暂时无法存取cookie
③php.ini中的session.use_trans_sid = 0或者编译时没有打开--enable-trans-sid选项
为什么会这样呢?下面解释一下原因:
Session文件分为两部分:session变量保存在服务器端(默认以文件方式存储session);而session id则以cookie形式保存在客户端。(注意:session默认是基于cookie的)。
当用户的浏览器向服务器提出请求时,同时发送包含session id的cookie(默认情况下)。服务器根据客户端提供的session id来得到用户的文件,即保存在服务器端的session变量值。事实上,session id可以使用客户端的Cookie或者Http1.1协议的Query_String(就是访问的URL的“?”后面的部分)来传送给服务器,然后服务器读取Session的目录。PHP中的session在默认情况下是使用客户端的Cookie来保存session id的,所以当客户端的cookie出现问题的时候就会影响session了。必须注意的是:session不一定必须依赖cookie,这也是session相比cookie的高明之处。
当客户端的Cookie被禁用或出现问题时,PHP会自动把session id附着在URL中,这样再通过session id就能跨页使用session变量了。但这种附着也是有一定条件的:
其一:“php.ini中的session.use_trans_sid = 1或者编译时打开打开了--enable-trans-sid选项”;
其二:运行PHP的服务器必须是unix/linux系统,windows不具备此项功能。
明白了以上的道理,我们就可以得出解决session跨页传递问题的三条途径:
1、设置php.ini中的session.use_trans_sid = 1或者编译时打开打开了--enable-trans-sid选项,让PHP自动跨页传递session id。
2、手动通过URL传值、隐藏表单传递session id。
3、用文件、数据库等形式保存session_id,在跨页过程中手动调用。
Java代码
<?php
session_start();
$_SESSION['var1']="中华人民共和国";
$sn = session_id();
$url='<a href="s2.php?s='.$sn.'">下一页</a>';//客户端不支持cookie时,使用该办法传递session.
echo $url;
?>
s2.php
Java代码
<?php
session_id($_GET['s']);
session_start();
echo "传递的session变量var1的值为:".$_SESSION['var1'];
?>
二,什么cookie
cookie分为二种
1,以文件方式存在硬盘空间上的长期性的cookie
2,停留在浏览器所占内存中的临时性的cookie
浏览网站时,你会经常发现网站登录的地方,会有提示,问你是不是要记住自己的登录状态,像这种情况,登录时填写的一些信息会被以文件的方式存放在客户端的硬盘上。
当用户登录后,session会在cookie端产生一个session_id,这个session_id是存于浏览器所占用的内存当中。当你关闭浏览器后,session_id也要消失了。
cookie采用的是在客户端保持状态的方案,它是客户端的会话状态的一种储存机制。它 是服务器在本地机器上存储的小段文本或者是内存中的一段数据,并随每一个请求发送至同一个服务器。IETF RFC 2965 HTTP State Management Mechanism 是通用cookie规范。网络服务器用HTTP头信息向客户端发送cookies,在客户终端,浏览器解析这些cookies并将它们保存为一个本地文 件,或者本地内存中数据,它会自动将同一服务器的任何请求缚上这些cookies,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以 session机制借助于cookie机制来达到保存标识的目的,这样就可以解决HTTP协议无状态的缺陷。
Java代码
<?php
if ($_GET[out])
{ //注销cookie方法
setcookie('name', '');
setcookie('pwd', '');
echo "<script>location.href='cookie.php'</script>";
}
if ($_POST[uname] && $_POST[upwd]) { //cookie的配置
setcookie('name', $_POST[uname], time() + 3600);
setcookie('pwd', $_POST[upwd], time() + 3600);
//让它跳转一次进行刷新
echo "<script>location.href='cookie.php'</script>";
}
if ($_COOKIE[name] && $_COOKIE[pwd]) { //cookie的应用
echo '用户:' . $_COOKIE[name] . '<br>密码:' . $_COOKIE[pwd];
//写个超级链接注销cookie
echo "<br><br><a href='cookie.php?out=out'>注销cookie</a>";
} // else echo "cookie保存失败!";
else echo "请登录!!!";
?>
<form action='' method="post">
用户:<input type="text" name='uname' value=''><br><br>
密码:<input type="text" name='upwd'><br><br>
<input type="submit" name='sub' value='登录'>
</form>
二级域名共享cookies
我们通常在使用cookie的时候一般都只是局限在本站内使用,也就是只在一个域名下使用。
如我们要在www.xhbin.com下使用一个cookie的话 ,只要在这个站下面的文件中设置一个cookie就行了
但是如果我们要想实现在一级域名下设置的cookie,同时要在二级域名下使用的话那么该怎么做呢??
比如这样的情况:我们在一级域名是个网站如:www.xhbin.com,他的下面有个二级域名如:bbs.xhbin.com
我们要想实现在一级域名上登录后,保留这个cookie到二级域名上,也就是说,实现同步登录的效果,
那么我们该怎么设置这个cookie??其实很简单。
就拿刚刚那个域名(www.xhbin.com)来说,我们可以这样设置cookie:setcookie(“fangbinbin”,”mengfei”,time()+3600,”/”,”.xhbin.com”);
那么我们就可以在这www.xhbin.com 和bbs.xhbin.com下面直接使用(echo $_COOKIE['fangbinbin']; )就能够输出那个设置的cookie的值了
这样子就解决了那个同步登录的问题!!当然如果涉及到3个及其以上的域名,最好都有统一的登录界面和退出界面,如登录时统一POST到一级域名的登录页面。
再将这个Cookie的所涉及到的用户登录Session信息保存到Key-Value类型的缓存中,如memcached、 memcachedDB 等,尽量避免写文件或者DB降低IO,提供用户体验。session存cookie
session 的生命周期是有一次,但是不同的页面要用同一个session变量的话,就需要在不同的页面加上session_start(),不然你是在另一个页面用 不了session的,所以为了方便就用session_set_cookie_params把session变量存在cookie中就可以正常使用 session了 ,这样就不用在每个页面都要调用session_start()
Java代码
session_save_path($path);
session_set_cookie_params(0, '/', '.trip166.com');
session_start();
然后在firefox下web Developer 中的查看cookie看你的session 的id的作用域,如果为.trip166.com,则说明cookie有效,session_id 全域有效,不出意外,你能取得session。
cookie安全 cookiekey和浏览器挂钩
Php代码
$auth_key = md5($salt.$_SERVER['HTTP_USER_AGENT']);
然后加密解密$auth_key,判断当前的和cookie中的$auth_key
PHP - 利用P3P实现跨域
Java代码
<?php
header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');
setcookie("test", '124', time()+3600, "/", ".a.com");
?>
深入分析java web技术内幕----读书笔记(六)
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a724888/article/details/63683030
这位大侠,这是我的公众号:程序员江湖。 分享程序员面试与技术的那些事。 干货满满,关注就送。
session和cookie都是用来保持用户与后端服务器的交互状态。
cookie大小大小受限,并且占带宽。
session不能在多个服务器间共享。
cookie是保存在客户端的一个数据结构
session是保存在服务端的一个数据结构。
服务端:在tomcat中使用addcookie方法添加cookie,真正则是在response中完成创建。
接受客户端cookie:通过request.getcookies()来获得。
request和response
Request 和 Response 对象起到了服务器与客户机之间的信息传递作用。Request 对象用于接收客户端浏览器提交的数据,而 Response 对象的功能则是将服务器端的数据发送到客户端浏览器。
session对cookie的改进:
cookie在每次访问时需要传回,增加传输量,可以设置id作为标志进行传递,即session id是通过cookie传递的,属性值为jsessionid。
有了seesionid,服务端可以创建httpsession对象。第一次触发通过request.getsession()。以后都通过session id来找到session。
session没过期时,即使容器关闭或重启也会被持久化。
同个浏览器打开同一个应用多次会使用同一个session,不同浏览器打开一个应用会用不同的session。
cookie问题:客户端存储限制、cookie管理混乱,cookie不能跨域名,安全问题。
可以用分布式session框架解决。
实现方式:
1统一使用订阅服务器管理cookie,应用服务器需要哪个cookie由订阅服务器来推送。一般用zookeeper实现
集群的配置管理。
2session将保存到分布式缓存中,让服务器可以共享访问,因为session不可能同步到每个服务器上。
分布式缓存可以用memcache实现。
3cookie太大的办法是压缩cookie,将keyvalue转化为文本。
4多终端session统一:手机和电脑的cookie和session需要统一,即包括数据结构,存储。防止二次登陆等问题。所以需要在服务端统一session,所有终端拿到的session id都一样。
5:热门。电脑打开网页扫码登录
服务端不停检测标识,手机登录后产生标识使pc端登录成功。
cas原理介绍
CAS用来标志用户的token是存在它自己的域名下的,不是存在web应用的域名下的.CAS判断用户登录由agent决定,agent验证用户信息有两种处理. 1.如果这个用户是以前没登录过的,也就是说这个web应用的域名下没有一个agent颁发的sessionid(在cookie里)的话,那么它会跳转到CAS server的登录界面(这里由CAS server决定是否显示登录界面,还是直接就跳转回来,也就是不是每次都要求输入密码登录的). 2.CAS的登录界面会有两种处理.如果以前没登录过,也就是在CAS server自己的域名下,没有用户的token的话,就要求登录.如果有token的话,就返回一个ticket(也就是不显示登录界面直接重定向返回了). 然后接第一步里面,agent收到ticket以后就提交给CAS server验证,并取得返回信息(是个xml,cas的协议规定的,也可以用saml之类的,这个地方可以取得除了用户姓名以外的信息,这样就可以做同步了,权限的问题可以这么解决).然后agent就保存此时的session,把sessionid放到该web应用域名下的cookie里. 以上逻辑说明了,标志用户身份的token在CAS server的域名下,每个web应用不保存用户身份.因此跨域是绝对没问题的.只是single sign off就需要CAS server的配合了,要把那个token删掉. 不过如果标志身份的token不存在,而sessionid存在,那还是被认为是登录的,只不过是在等待那个session timeout而已.
本文来自云栖社区合作伙伴“doNET跨平台”,了解相关信息可以关注“opendotnet”微信公众号
负载均衡SLB高可用的四个层次
负载均衡支持对多台ECS进行流量分发,以提升应用系统的服务能力,长期以来都是关键业务系统的入口。淘宝,天猫,阿里云等无不依赖负载均衡产品,双11的流量洪峰也依赖负载均衡的调度和处理能力。
负载均衡SLB简单介绍
下图是负载均衡的简单示意图,用户的访问请求经过SLB实例的一个监听(端口),再被转发到后端的ECS上。SLB实例对应一个IP地址,监听就是实例上IP地址的一个端口,流量调度是基于监听(端口)进行的,ECS是真正处理服务请求的。
负载均衡SLB架构
下图是从流量转发路径来看的负载均衡SLB的架构图,可以称为一个负载均衡SLB的集群,这个集群部署在华东1的两个可用区,每个可用区都部署了LVS集群和Tengine集群,其中LVS集群负责接收所有流量的请求,包括TCP/UDP/HTTP/HTTPS,对于TCP和UDP请求,LVS集群会直接将流量转发到后端ECS,对于HTTP/HTTPS 请求会将流量转发给Tengine集群,再转发到后端ECS上。
负载均衡SLB高可用的四个层次
为了便于理解和说明,可以将负载均衡SLB高可用划分为四个层次,如图上图所示,第1层是应用处理层,即真正处理请求的ECS层。第2层是集群转发层,即在集群转发层面要保证高可用,第3层是跨可用区容灾层,在出现可用区级别的故障时可以切换,第4层是跨地域容灾层,上图未标示。下面分别从这4个层次进行分析和说明。
特别要说明的是,产品设计上SLB会尽可能做到高可用,但如果用户系统没有高可用设计,最终也不可能实现真正的高可用,而且除了负载均衡,还有数据库等的高可用问题,因此产品设计的高可用和用户系统设计高可用必须结合起来。下面4个层次的说明也会分别从产品设计和用户使用两个角度进行分析和解读。
第一层 应用处理层
应用处理层是真正处理用户请求的,一般将应用部署在ECS上。
从产品设计角度看:
应用处理层的高可用主要有下面两点,一是要保证ECS出现故障时能及时屏蔽,避免将流量转发到故障节点从而影响用户访问,SLB产品是通过健康检查功能实现的。二是SLB实例能够添加多台ECS,特别是可以添加不同可用区的ECS,从而避免一个可用区的ECS均不可用时影响用户访问。这两点目前SLB产品都可以做到。
从用户使用角度看:
首先,用户一定要开启并正确配置健康检查。SLB支持TCP/UDP/HTTP/HTTPS 四种协议,其中对TCP协议提供了TCP和HTTP两种健康检查方式,用户可以根据需要选择。对UDP协议,用户可以定义UDP健康检查端口,还可以根据自己定义健康检查请求和返回值来判断健康检查结果。对于HTTP和HTTPS协议,默认使用HTTP健康检查,用户可以定义一个健康检查URL,负载均衡的健康检查模块会通过HTTP HEAD来探测获取状态。关于健康检查详细信息可以参考健康检查原理说明
其次,用户要考虑到单可用区ECS都不可用的情况,选择多个可用区的ECS添加到负载均衡SLB的实例中。用户可能有疑问,如果一个可用区的ECS不可用了,那这个可用区的负载均衡是不是也会不可用?这个问题在稍后的第三层来说明。
第二层 集群转发层
集群转发层指的是负载转发用户请求的SLB集群,包括LVS 集群和Tengine集群,不管是机器故障还是集群升级,都要保障用户请求尽可能不中断,
从产品设计角度看:
首先,必须避免单点故障,如果采用传统的主备切换模式一方面在切换的时候可能影响用户请求,另一方面主备切换还是依赖单机的处理能力,无法很好的横向扩充,因此,转发层不管是LVS还是Tengine集群都集群部署,并通过上层交换机的ECMP等价路由还实现用户请求到集群多台机器的流量转发。看上面的架构图可能用户有疑问,TENGINE集群是接在LVS集群后面的,上层交换机的ECMP如何起作用呢?其实LVS集群对Tengine集群也是有健康检查机制的,即如果Tengine集群的机器出现异常,LVS健康检查发现后会将这台异常的Tengine机器从集群中摘除。
其次,在集群中机器出现down机等异常时尽可能不影响用户请求。有了集群部署后,集群中的机器出现软硬件故障还是难以避免的,有可能机器彻底不可用了或者有异常了需要通过运维手段将这台机器从集群中移出并维修,这个时候这台机器上的用户请求要尽可能做到不中断。
SLB集群是通过Session同步来实现集群中有机器故障时尽可能不影响用户请求的。如下图所示,当用户的访问请求经过集群中的一台LVS时,集群会根据预设的规则将Session同步到其它LVS机器,从而实现LVS集群所有机器都有该Session,当如下图中的LVS1机器出现故障或需要维护时,用户请求就会通过其它LVS机器进行流量转发并且用户基本不感知。但是有Session同步不代表就解决了所有问题,Session同步能解决大部分长连接的问题,但是对于短链接,连接未建立(三次握手未完成)或者已建立连接但是还没有触发Session同步规则时,机器故障还是可能会影响用户请求。因此,需要用户的系统和程序进行配合。
从用户使用角度看,一定要在代码中加上相应的重试机制! 这样在上述情况出现时,会进一步降低对用户访问影响。
第三层 跨可用区容灾层
上面说的是在一个可用区内负载均衡转发集群的高可用,跨可用容灾层则要解决的是当一个可用区都不可用时,还能继续使用另外一个可用区的负载均衡继续提供服务。
从产品设计角度看:
还是如看SLB架构图,首先,负载均衡集群要实现跨可用区部署。另外,还需要一种机制能及时发现其中某个可用区,如可用区A都出现了故障然后切换到另外一个可用区B继续服务。从技术上讲,使用了路由探测和路由优先级机制来实现,即可用区A的一段地址(对应一批SLB实例1-N)除了在本可用区的集群上配置外,其实底层还在可用区B的集群上也有这段地址(对应一批SLB实例1-N),只不过在正常情况下,由于可用区A的这段地址路由优先级更高,所以流量都从可用区A的集群进行转发,如果路由探测到整个可用区A不可用(路由不可达)或者可用区A的某段地址不可用(路由不可达),则路由到可用区B的集群进行流量转发。
从用户对产品形态的感知上来说,就是主可用区和备可用区,但实例上的这个主备可用区用户选择后就无法更改了,用户也无法自行切换主备关系。也就是说主备可用区是底层的机制,用户一旦选择无法干预了。暴露主备可用区概念的原因一方面希望用户在使用SLB和其它有可用区属性的产品进行组合的时候,能做到按需组合,如ECS可用区,RDS的主备可用区等。另一方面也是希望用户更多认知到产品在高可用方面的价值。不过在实际使用时,用户往往存在误区,这些误区使我们考虑是不是要关闭备可用区概念,只对用户暴露主可用区,后面再来解释这些误区。
回过头来说跨可用区高可用,其实,最理想的情况是一个可用区的一个SLB实例出现异常(路由不可达)时,系统就能够自动切换或者用户能手动切换到另外一个可用区的SLB实例继续服务。但不能这样做的主要原因是需要发布明细路由,即32位路由,而这在云环境中是无法接受的。
从用户使用角度看:
首先,跨可用区容灾需要保证一个SLB实例的后端服务器ECS分布在多个可用区,即避免一个可用区不可用时,SLB后端的ECS都无法使用从而影响用户访问,这点在第一层 应用处理层中已经说明了。当然,如果还使用DB等产品,还需要考虑DB的跨可用区容灾问题,用户可以参考DB相关产品的说明。这里主要谈负载均衡本身以及和负载均衡紧密相关的后端服务器ECS的高可用问题。
其次,对于重要的业务,一定要使用至少两个且主可用区不一样的实例来容灾,这点非常重要。如重要的用户注册系统,如下图所示,在华东1的可用区A和可用区B分布部署一个SLB实例,都挂载了两个可用区的相同ECS。
用户可能会有两个疑问,一是负载均衡SLB产品本身有跨可用区切换能力,为什么注册系统还需要自己在两个可用区建立两个实例进行跨可用区容灾呢?负载均衡SLB产品本身的跨可用区切换是在非常极端的情况下(整个可用区不可用/所有LVS集群上的IP无法路由/某个地址段都无法路由)才切换,而对于比如仅一个可用区的Tengine有部分异常、LVS集群有异常但是IP还可以Ping通等相对不那么极端又会影响用户业务的情况下是不起作用的,因此,重要的业务系统非常有必要用户自己在两个可用区建立实例容灾。
二是注册系统使用的两个SLB实例如何调度,用户可以使用云解析将注册系统的域名解析到上面的两个SLB实例,其它的相关域名解析系统也都支持这样的功能。如果是私网SLB实例,阿里云正在研发私网DNS系统,当前用户可以自己构建一个私网DNS系统或者通过程序来实现调度。
再次强调一下重要业务系统用户自行在两个可用区建立实例容灾的必要性。哪怕正常情况下有一个实例就配置好不使用,虽然需要支付每小时2分钱的公网IP费用,但当出现其中一个实例所在集群异常并且恢复时间较长时,用户也可以自行通过修改DNS解析或者程序调用地址来快速恢复业务。
最后,再来说下用户对于主备可用区可能存在的误区
1)认为主备可用区就是只要一个实例有异常了负载均衡就能自动切换到备可用区
2)认为主备可用区切换的异常包括个别实例无法Ping、实例上的服务异常等等很多条件
其实,负载均衡主备可用区切换设计是在极端情况下(整个可用区不可用/所有LVS集群上的IP无法路由/某个地址段都无法路由)才会自动进行的,它既不是某个IP地址无法路由就切换,也不是某个或者某些IP地址可以Ping但这些IP的服务异常就切换。因此,用户系统通过自身在多个可用区建立实例实现跨可用区灾是非常必要的。
第四层 跨地域容灾层
最后来说下跨地域容灾层。随着业务的发展,用户对业务系统的高可用要求越来越高,已经不满足于只能做到跨可用区的容灾,用户希望即使某个地域的系统都不可用了,还可用通过其它地域的系统继续提供服务,这就是跨地域容灾。
跨地域容灾是个非常大的课题,不仅仅涉及到网络层面,更涉及到应用系统的改造和适配,数据的同步和一致性等很棘手的问题。这里仅说明下网络层面的跨地域容灾。从产品角度看,跨可用区容灾一般是通过DNS来实现的,如下图所示
传统的如F5的全局负载均衡(以前叫GTM,现在叫BIG-IP DNS)就有比较完善的解决方案,或者一些提供DNS服务的系统也有类似的功能。负载均衡SLB产品本身没有提供这样的能力,跨地域容灾的能力是通过云解析DNS产品来实现的,云解析DNS产品提供了全局负载均衡的能力,还有如健康检查,路由调度优化等功能,可以参考 全球负载均衡跨地域容灾解决方案
另外,对于跨可用区容灾可能需要使用在不同地域间同步数据或者跨地域私网调用,可以使用高速通道产品构建不同地域的通信链路
从用户角度看,跨可用区容灾网络层面主要通过云解析DNS产品,对于不同地域间通信可以使用高速通道产品,这两个产品不是本文的重点,可以查看阿里云官网的相关产品文档说明。
还分不清 Cookie、Session、Token、JWT?
什么是认证(Authentication)通俗地讲就是验证当前用户的身份,证明“你是你自己”(比如:你每天上下班打卡,都需要通过指纹打卡,当你的指纹和系统里录入的指纹相匹配时,就打卡成功)互联网中的认证:用户名密码登录邮箱发送登录链接手机号接收验证码只要你能收到邮箱/验证码,就默认你是账号的主人什么是授权(Authorization)用户授予第三方应用访问该用户某些资源的权限你在安装手机应用的时候,APP 会询问是否允许授予权限(访问相册、地理位置等权限)你在访问微信小程序时,当登录时,小程序会询问是否允许授予权限(获取昵称、头像、地区、性别等个人信息)实现授权的方式有:cookie、session、token、OAuth什么是凭证(Credentials)实现认证和授权的前提是需要一种媒介(证书) 来标记访问者的身份在战国时期,商鞅变法,发明了照身帖。照身帖由官府发放,是一块打磨光滑细密的竹板,上面刻有持有人的头像和籍贯信息。国人必须持有,如若没有就被认为是黑户,或者间谍之类的。在现实生活中,每个人都会有一张专属的居民身份证,是用于证明持有人身份的一种法定证件。通过身份证,我们可以办理手机卡/银行卡/个人贷款/交通出行等等,这就是认证的凭证。在互联网应用中,一般网站(如掘金)会有两种模式,游客模式和登录模式。游客模式下,可以正常浏览网站上面的文章,一旦想要点赞/收藏/分享文章,就需要登录或者注册账号。当用户登录成功后,服务器会给该用户使用的浏览器颁发一个令牌(token),这个令牌用来表明你的身份,每次浏览器发送请求时会带上这个令牌,就可以使用游客模式下无法使用的功能。什么是 CookieHTTP 是无状态的协议(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息):每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。所以服务器与浏览器为了进行会话跟踪(知道是谁在访问我),就必须主动的去维护一个状态,这个状态用于告知服务端前后两个请求是否来自同一浏览器。而这个状态需要通过 cookie 或者 session 去实现。cookie 存储在客户端:cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。cookie 是不可跨域的:每个 cookie 都会绑定单一的域名,无法在别的域名下获取使用,一级域名和二级域名之间是允许共享使用的(靠的是 domain)。cookie 重要的属性什么是 Sessionsession 是另一种记录服务器和客户端会话状态的机制session 是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中session 认证流程:用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。根据以上流程可知,SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。Cookie 和 Session 的区别安全性:Session 比 Cookie 安全,Session 是存储在服务器端的,Cookie 是存储在客户端的。存取值的类型不同:Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型。有效期不同:Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。存储大小不同:单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。什么是 Token(令牌)Acesss Token访问资源接口(API)时所需要的资源凭证简单 token 的组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)特点:服务端无状态化、可扩展性好支持移动端设备安全支持跨程序调用token 的身份验证流程:客户端使用用户名跟密码请求登录服务端收到请求,去验证用户名与密码验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里客户端每次向服务端请求资源的时候需要带着服务端签发的 token服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用解析 token 的计算时间换取 session 的存储空间,从而减轻服务器的压力,减少频繁的查询数据库token 完全由应用管理,所以它可以避开同源策略Refresh Token另外一种 token——refresh tokenrefresh token 是专用于刷新 access token 的 token。如果没有 refresh token,也可以刷新 access token,但每次刷新都要用户输入登录用户名与密码,会很麻烦。有了 refresh token,可以减少这个麻烦,客户端直接用 refresh token 去更新 access token,无需用户进行额外的操作。Access Token 的有效期比较短,当 Acesss Token 由于过期而失效时,使用 Refresh Token 就可以获取到新的 Token,如果 Refresh Token 也失效了,用户就只能重新登录了。Refresh Token 及过期时间是存储在服务器的数据库中,只有在申请新的 Acesss Token 时才会验证,不会对业务接口响应时间造成影响,也不需要向 Session 一样一直保持在内存中以应对大量的请求。Token 和 Session 的区别Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。而 Token 是令牌,访问资源接口(API)时所需要的资源凭证。Token 使服务端无状态化,不会存储会话信息。Session 和 Token 并不矛盾,作为身份认证 Token 安全性比 Session 好,因为每一个请求都有签名还能防止监听以及重放攻击,而 Session 就必须依赖链路层来保障通讯安全了。如果你需要实现有状态的会话,仍然可以增加 Session 来在服务器端保存一些状态。所谓 Session 认证只是简单的把 User 信息存储到 Session 里,因为 SessionID 的不可预测性,暂且认为是安全的。而 Token ,如果指的是 OAuth Token 或类似的机制的话,提供的是 认证 和 授权 ,认证是针对用户,授权是针对 App 。其目的是让某 App 有权利访问某用户的信息。这里的 Token 是唯一的。不可以转移到其它 App上,也不可以转到其它用户上。Session 只提供一种简单的认证,即只要有此 SessionID ,即认为有此 User 的全部权利。是需要严格保密的,这个数据应该只保存在站方,不应该共享给其它网站或者第三方 App。所以简单来说:如果你的用户数据可能需要和第三方共享,或者允许第三方调用 API 接口,用 Token 。如果永远只是自己的网站,自己的 App,用什么就无所谓了。什么是 JWTJSON Web Token(简称 JWT)是目前最流行的跨域认证解决方案。是一种认证授权机制。JWT 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准(RFC 7519)。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上。可以使用 HMAC 算法或者是 RSA 的公/私秘钥对 JWT 进行签名。因为数字签名的存在,这些传递的信息是可信的。JWT 的原理JWT 认证流程:用户输入用户名/密码登录,服务端认证成功后,会返回给客户端一个 JWT客户端将 token 保存到本地(通常使用 localstorage,也可以使用 cookie)当用户希望访问一个受保护的路由或者资源的时候,需要请求头的 Authorization 字段中使用Bearer 模式添加 JWT,其内容看起来是下面这样Authorization: Bearer <token>服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息,如果合法,则允许用户的行为因为 JWT 是自包含的(内部包含了一些会话信息),因此减少了需要查询数据库的需要因为 JWT 并不使用 Cookie 的,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)因为用户的状态不再存储在服务端的内存中,所以这是一种无状态的认证机制JWT 的使用方式客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。方式一当用户希望访问一个受保护的路由或者资源的时候,可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求头信息的 Authorization 字段里,使用 Bearer 模式添加 JWT。
GET /calendar/v1/events
Host: api.example.com
Authorization: Bearer <token>用户的状态不会存储在服务端的内存中,这是一种 无状态的认证机制服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息,如果合法,则允许用户的行为。由于 JWT 是自包含的,因此减少了需要查询数据库的需要JWT 的这些特性使得我们可以完全依赖其无状态的特性提供数据 API 服务,甚至是创建一个下载流服务。因为 JWT 并不使用 Cookie ,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)方式二跨域的时候,可以把 JWT 放在 POST 请求的数据体里。方式三通过 URL 传输http://www.example.com/user?token=xxxToken 和 JWT 的区别相同:都是访问资源的令牌都可以记录用户的信息都是使服务端无状态化都是只有验证成功后,客户端才能访问服务端上受保护的资源区别:Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。JWT:将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。常见的前后端鉴权方式Session-CookieToken 验证(包括 JWT,SSO)OAuth2.0(开放授权)常见的加密算法哈希算法(Hash Algorithm)又称散列算法、散列函数、哈希函数,是一种从任何一种数据中创建小的数字“指纹”的方法。哈希算法将数据重新打乱混合,重新创建一个哈希值。哈希算法主要用来保障数据真实性(即完整性),即发信人将原始消息和哈希值一起发送,收信人通过相同的哈希函数来校验原始数据是否真实。哈希算法通常有以下几个特点:2 的 128 次方为 340282366920938463463374607431768211456,也就是 10 的 39 次方级别2 的 160 次方为 1.4615016373309029182036848327163e+48,也就是 10 的 48 次方级别2 的 256 次方为 1.1579208923731619542357098500869 × 10 的 77 次方,也就是 10 的 77 次方正像快速:原始数据可以快速计算出哈希值逆向困难:通过哈希值基本不可能推导出原始数据输入敏感:原始数据只要有一点变动,得到的哈希值差别很大冲突避免:很难找到不同的原始数据得到相同的哈希值,宇宙中原子数大约在 10 的 60 次方到 80 次方之间,所以 2 的 256 次方有足够的空间容纳所有的可能,算法好的情况下冲突碰撞的概率很低:注意:以上不能保证数据被恶意篡改,原始数据和哈希值都可能被恶意篡改,要保证不被篡改,可以使用RSA 公钥私钥方案,再配合哈希值。哈希算法主要用来防止计算机传输过程中的错误,早期计算机通过前 7 位数据第 8 位奇偶校验码来保障(12.5% 的浪费效率低),对于一段数据或文件,通过哈希算法生成 128bit 或者 256bit 的哈希值,如果校验有问题就要求重传。常见问题使用 cookie 时需要考虑的问题因为存储在客户端,容易被客户端篡改,使用前需要验证合法性不要存储敏感数据,比如用户密码,账户余额使用 httpOnly 在一定程度上提高安全性尽量减少 cookie 的体积,能存储的数据量不能超过 4kb设置正确的 domain 和 path,减少数据传输cookie 无法跨域一个浏览器针对一个网站最多存 20 个Cookie,浏览器一般只允许存放 300 个Cookie移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token使用 session 时需要考虑的问题将 session 存储在服务器里面,当用户同时在线量比较多时,这些 session 会占据较多的内存,需要在服务端定期的去清理过期的 session当网站采用集群部署的时候,会遇到多台 web 服务器之间如何做 session 共享的问题。因为 session 是由单个服务器创建的,但是处理用户请求的服务器不一定是那个创建 session 的服务器,那么该服务器就无法拿到之前已经放入到 session 中的登录凭证之类的信息了。当多个应用要共享 session 时,除了以上问题,还会遇到跨域问题,因为不同的应用可能部署的主机不一样,需要在各个应用做好 cookie 跨域的处理。sessionId 是存储在 cookie 中的,假如浏览器禁止 cookie 或不支持 cookie 怎么办?一般会把 sessionId 跟在 url 参数后面即重写 url,所以 session 不一定非得需要靠 cookie 实现移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token使用 token 时需要考虑的问题如果你认为用数据库来存储 token 会导致查询时间太长,可以选择放在内存当中。比如 redis 很适合你对 token 查询的需求。token 完全由应用管理,所以它可以避开同源策略token 可以避免 CSRF 攻击(因为不需要 cookie 了)移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token使用 JWT 时需要考虑的问题因为 JWT 并不依赖 Cookie 的,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)。JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。JWT 不加密的情况下,不能将秘密数据写入 JWT。JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。JWT 最大的优势是服务器不再需要存储 Session,使得服务器认证鉴权业务可以方便扩展。但这也是 JWT 最大的缺点:由于服务器不需要存储 Session 状态,因此使用过程中无法废弃某个 Token 或者更改 Token 的权限。也就是说一旦 JWT 签发了,到期之前就会始终有效,除非服务器部署额外的逻辑。JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。JWT 适合一次性的命令认证,颁发一个有效期极短的 JWT,即使暴露了危险也很小,由于每次操作都会生成新的 JWT,因此也没必要保存 JWT,真正实现无状态。为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。使用加密算法时需要考虑的问题绝不要以明文存储密码永远使用 哈希算法 来处理密码,绝不要使用 Base64 或其他编码方式来存储密码,这和以明文存储密码是一样的,使用哈希,而不要使用编码。编码以及加密,都是双向的过程,而密码是保密的,应该只被它的所有者知道, 这个过程必须是单向的。哈希正是用于做这个的,从来没有解哈希这种说法, 但是编码就存在解码,加密就存在解密。绝不要使用弱哈希或已被破解的哈希算法,像 MD5 或 SHA1 ,只使用强密码哈希算法。绝不要以明文形式显示或发送密码,即使是对密码的所有者也应该这样。如果你需要 “忘记密码” 的功能,可以随机生成一个新的 一次性的(这点很重要)密码,然后把这个密码发送给用户。分布式架构下 session 共享方案1. session 复制任何一个服务器上的 session 发生改变(增删改),该节点会把这个 session 的所有内容序列化,然后广播给所有其它节点,不管其他服务器需不需要 session ,以此来保证 session 同步优点:可容错,各个服务器间 session 能够实时响应。缺点:会对网络负荷造成一定压力,如果 session 量大的话可能会造成网络堵塞,拖慢服务器性能。2. 粘性 session /IP 绑定策略采用 Ngnix 中的 ip_hash 机制,将某个 ip的所有请求都定向到同一台服务器上,即将用户与服务器绑定。用户第一次请求时,负载均衡器将用户的请求转发到了 A 服务器上,如果负载均衡器设置了粘性 session 的话,那么用户以后的每次请求都会转发到 A 服务器上,相当于把用户和 A 服务器粘到了一块,这就是粘性 session 机制。优点:简单,不需要对 session 做任何处理。缺点:缺乏容错性,如果当前访问的服务器发生故障,用户被转移到第二个服务器上时,他的 session 信息都将失效。适用场景:发生故障对客户产生的影响较小;服务器发生故障是低概率事件 。实现方式:以 Nginx 为例,在 upstream 模块配置 ip_hash 属性即可实现粘性 session。3. session 共享(常用)使用分布式缓存方案比如 Memcached 、Redis 来缓存 session,但是要求Memcached 或 Redis 必须是集群把 session 放到 Redis 中存储,虽然架构上变得复杂,并且需要多访问一次 Redis ,但是这种方案带来的好处也是很大的:实现了 session 共享;可以水平扩展(增加 Redis 服务器);服务器重启 session 不丢失(不过也要注意 session 在 Redis 中的刷新/失效机制);不仅可以跨服务器 session 共享,甚至可以跨平台(例如网页端和 APP 端)4. session 持久化将 session 存储到数据库中,保证 session 的持久化优点:服务器出现问题,session 不会丢失缺点:如果网站的访问量很大,把 session 存储到数据库中,会对数据库造成很大压力,还需要增加额外的开销维护数据库。只要关闭浏览器 ,session 真的就消失了?不对。对 session 来说,除非程序通知服务器删除一个 session,否则服务器会一直保留,程序一般都是在用户做 log off 的时候发个指令去删除 session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是大部分 session 机制都使用会话 cookie 来保存 session id,而关闭浏览器后这个 session id 就消失了,再次连接服务器时也就无法找到原来的 session。如果服务器设置的 cookie 被保存在硬盘上,或者使用某种手段改写浏览器发出的 HTTP 请求头,把原来的 session id 发送给服务器,则再次打开浏览器仍然能够打开原来的 session。恰恰是由于关闭浏览器不会导致 session 被删除,迫使服务器为 session 设置了一个失效时间,当距离客户端上一次使用 session 的时间超过这个失效时间时,服务器就认为客户端已经停止了活动,才会把 session 删除以节省存储空间。
Tomcat+Servlet面试题都在这里(下)
Servlet相关 APIdoGet与doPost方法的两个参数是什么HttpServletRequest:封装了与请求相关的信息HttpServletResponse:封装了与响应相关的信息获取页面的元素的值有几种方式,分别说一下request.getParameter() 返回客户端的请求参数的值request.getParameterNames() 返回所有可用属性名的枚举request.getParameterValues() 返回包含参数的所有值的数组request.getAttribute()和request.getParameter()区别用途上:request.getAttribute(), 一般用于获取request域对象的数据(在跳转之前把数据使用setAttribute来放到request对象上)request.getParameter(), 一般用于获取客户端提交的参数存储数据上:request.getAttribute()可以获取Objcet对象request.getParameter()只能获取字符串(这也是为什么它一般用于获取客户端提交的参数)forward和redirect的区别forward和redirect的区别实际发生位置不同,地址栏不同重定向是由浏览器进行跳转的,进行重定向跳转的时候,浏览器的地址会发生变化的。曾经介绍过:实现重定向的原理是由response的状态码和Location头组合而实现的。这是由浏览器进行的页面跳转实现重定向会发出两个http请求,**request域对象是无效的,因为它不是同一个request对象转发是由服务器进行跳转的,细心的朋友会发现,在转发的时候,浏览器的地址栏是没有发生变化的,在我访问Servlet111的时候,即使跳转到了Servlet222的页面,浏览器的地址还是Servlet111的。也就是说浏览器是不知道该跳转的动作,转发是对浏览器透明的。通过上面的转发时序图我们也可以发现,实现转发只是一次的http请求,一次转发中request和response对象都是同一个。这也解释了,为什么可以使用request作为域对象进行Servlet之间的通讯。转发是发生在服务器的重定向是发生在浏览器的用法不同:重定向时"/"代表的是webapps目录转发时"/"代表的是本应用程序的根目录【zhongfucheng】很多人都搞不清楚转发和重定向的时候,资源地址究竟怎么写。有的时候要把应用名写上,有的时候不用把应用名写上。很容易把人搞晕。记住一个原则: 给服务器用的直接从资源名开始写,给浏览器用的要把应用名写上request.getRequestDispatcher("/资源名 URI").forward(request,response)response.send("/web应用/资源名 URI");能够去往的URL的范围不一样:转发是服务器跳转只能去往当前web应用的资源重定向是服务器跳转,可以去往任何的资源传递数据的类型不同转发的request对象可以传递各种类型的数据,包括对象重定向只能传递字符串跳转的时间不同转发时:执行到跳转语句时就会立刻跳转重定向:整个页面执行完之后才执行跳转那么转发(forward)和重定向(redirect)使用哪一个?根据上面说明了转发和重定向的区别也可以很容易概括出来。转发是带着转发前的请求的参数的。重定向是新的请求。典型的应用场景:转发: 访问 Servlet 处理业务逻辑,然后 forward 到 jsp 显示处理结果,浏览器里 URL 不变重定向: 提交表单,处理成功后 redirect 到另一个 jsp,防止表单重复提交,浏览器里 URL 变了tomcat容器是如何创建servlet类实例?用到了什么原理?tomcat容器是如何创建servlet类实例?用到了什么原理当容器启动时,会读取在webapps目录下所有的web应用中的web.xml文件,然后对 xml文件进行解析,并读取servlet注册信息。然后,将每个应用中注册的servlet类都进行加载,并通过 反射的方式实例化。(有时候也是在第一次请求时实例化)在servlet注册时加上1如果为正数,则在一开始就实例化,如果不写或为负数,则第一次请求实例化。什么是cookie?Session和cookie有什么区别?什么是cookie?Cookie是由W3C组织提出,最早由netscape社区发展的一种机制网页之间的交互是通过HTTP协议传输数据的,而Http协议是无状态的协议。无状态的协议是什么意思呢?一旦数据提交完后,浏览器和服务器的连接就会关闭,再次交互的时候需要重新建立新的连接。服务器无法确认用户的信息,于是乎,W3C就提出了:给每一个用户都发一个通行证,无论谁访问的时候都需要携带通行证,这样服务器就可以从通行证上确认用户的信息。通行证就是CookieSession和cookie有什么区别?从存储方式上比较Cookie只能存储字符串,如果要存储非ASCII字符串还要对其编码。Session可以存储任何类型的数据,可以把Session看成是一个容器从隐私安全上比较Cookie存储在浏览器中,对客户端是可见的。信息容易泄露出去。如果使用Cookie,最好将Cookie加密Session存储在服务器上,对客户端是透明的。不存在敏感信息泄露问题。从有效期上比较Cookie保存在硬盘中,只需要设置maxAge属性为比较大的正整数,即使关闭浏览器,Cookie还是存在的Session的保存在服务器中,设置maxInactiveInterval属性值来确定Session的有效期。并且Session依赖于名为JSESSIONID的Cookie,该Cookie默认的maxAge属性为-1。如果关闭了浏览器,该Session虽然没有从服务器中消亡,但也就失效了。从对服务器的负担比较Session是保存在服务器的,每个用户都会产生一个Session,如果是并发访问的用户非常多,是不能使用Session的,Session会消耗大量的内存。Cookie是保存在客户端的。不占用服务器的资源。像baidu、Sina这样的大型网站,一般都是使用Cookie来进行会话跟踪。从浏览器的支持上比较如果浏览器禁用了Cookie,那么Cookie是无用的了!如果浏览器禁用了Cookie,Session可以通过URL地址重写来进行会话跟踪。从跨域名上比较Cookie可以设置domain属性来实现跨域名Session只在当前的域名内有效,不可夸域名Servlet安全性问题由于Servlet是单例的,当多个用户访问Servlet的时候,服务器会为每个用户创建一个线程。当多个用户并发访问Servlet共享资源的时候就会出现线程安全问题。原则:如果一个变量需要多个用户共享,则应当在访问该变量的时候,加同步机制synchronized (对象){}如果一个变量不需要共享,则直接在 doGet() 或者 doPost()定义.这样不会存在线程安全问题
基于Kerberos的大数据安全方案
1.背景
互联网从来就不是一个安全的地方。很多时候我们过分依赖防火墙来解决安全的问题,不幸的是,防火墙是假设“坏人”是来自外部的,而真正具有破坏性的攻击事件都是往往都是来自于内部的。
近几年,在thehackernews等网站上总会时不时的看到可以看到一些因为数据安全问题被大面积攻击、勒索的事件。在Hadoop1.0.0之前,Hadoop并不提供对安全的支持,默认集群内所有角色都是可靠的。用户访问时不需要进行任何验证,++导致++恶意用户很容易就可以伪装进入集群进行破坏。
要保证Hadoop集群的安全,至少要做到2个A:Authentication(认证),Authorization(授权)。常见的方案有:
Authentication:MIT Kerberos, Azure AD, Kerby
Authorization:Apache Sentry(Cloudera), Apache Ranger(Hortonworks)
Hadoop集群对Kerberos的支持
2012年1.0.0版本正式发布后,Hadoop才增加了对Kerberos的支持, 使得集群中的节点是可信任的。
Kerberos可以将认证的密钥在集群部署时事先放到可靠的节点上。集群运行时,集群内的节点使用密钥得到认证,认证通过后的节点才能提供服务。企图冒充的节点由于没有事先得到的密钥信息,无法与集群内部的节点通信。这样就防止了恶意地使用或篡改Hadoop集群的问题,确保了Hadoop集群的可靠性、安全性。
2.Kerberos介绍
Kerberos是种网络身份验证协议,最初设计是用来保护雅典娜工程的网络服务器。Kerberos这个名字源于希腊神话,是一只三头犬的名字,它旨在通过使用密钥加密技术为Client/Server序提供强身份验证。可以用于防止窃听、防止重放攻击、保护数据完整性等场合,是一种应用对称密钥体制进行密钥管理的系统。Kerberos的扩展产品也使用公开密钥加密方法进行认证。
Kerberos目前最新版本是5,1~3版本只在MIT内部发行,因为使用DES加密,早期被美国出口管制局列为军需品禁止出口,直到瑞典皇家工学院实现了Kerberos版本4,KTH-KRB。后续也是这个团队实现了版本5: Heimdal,目前常见的Kerberos5实现之一。
本文中讨论的Kerberos5实现版本为MIT Kerberos,MIT保持的大约半年左右一次的更新速度,目前最新版本是2018-11-01发布的1.16.2版本。
2.1 名词解释
AS(Authentication Server):认证服务器
KDC(Key Distribution Center):密钥分发中心
TGT(Ticket Granting Ticket):票据授权票据,票据的票据
TGS(Ticket Granting Server):票据授权服务器
SS(Service Server):特定服务提供端
Principal:被认证的个体
Ticket:票据,客户端用来证明身份真实性。包含:用户名,IP,时间戳,有效期,会话秘钥。
使用Kerberos时,一个客户端需要经过三个步骤来获取服务:
认证: 客户端向认证服务器发送一条报文,获取一个包含时间戳的TGT。
授权: 客户端使用TGT向TGS请求指定Service的Ticket。
服务请求: 客户端向指定的Service出示服务Ticket鉴权通讯。
Kerberos协议在网络通信协定中属于显示层。其通信流程简单地说,用户先用共享密钥从某认证服务器得到一个身份证明。随后,用户使用这个身份证明与SS通信,而不使用共享密钥。
2.2 具体通信流程
①此流程使用了对称加密; ②此流程发生在某一个Kerberos领域中; ③小写字母c,d,e,g是客户端发出的消息,大写字母A,B,E,F,H是各个服务器发回的消息。
首先,用户使用客户端上的程序进行登录:
输入用户ID和密码到客户端(或使用keytab登录)。
客户端程序运行一个单向函数(大多数为Hash)把密码转换成密钥,这个就是客户端的“用户密钥”(user's secret key)。
2.2.1 客户端认证(Kinit)
客户端(Client)从认证服务器(AS)获取票据的票据(TGT)。
Client向AS发送1条明文消息,申请基于该用户所应享有的服务,例如“用户Sunny想请求服务”(Sunny是用户ID)。(注意:用户不向AS发送“用户密钥”(user's secret key),也不发送密码)该AS能够从本地数据库中查询到该申请用户的密码,并通过相同途径转换成相同的“用户密钥”(user's secret key)。
AS检查该用户ID是否在于本地数据库中,如果用户存在则返回2条消息:
【消息A】:Client/TGS会话密钥(Client/TGS Session Key)(该Session Key用在将来Client与TGS的通信(会话)上),通过 用户密钥(user's secret key) 进行加密。
【消息B】:票据授权票据(TGT)(TGT包括:消息A中的“Client/TGS会话密钥”(Client/TGS Session Key),用户ID,用户网址,TGT有效期),通过TGS密钥(TGS's secret key) 进行加密。
一旦Client收到消息A和消息B,Client首先尝试用自己的“用户密钥”(user's secret key)解密消息A,如果用户输入的密码与AS数据库中的密码不符,则不能成功解密消息A。输入正确的密码并通过随之生成的"user's secret key"才能解密消息A,从而得到“Client/TGS会话密钥”(Client/TGS Session Key)。(注意:Client不能解密消息B,因为B是用TGS密钥(TGS's secret key)加密的)。拥有了“Client/TGS会话密钥”(Client/TGS Session Key),Client就足以通过TGS进行认证了。
2.2.2 服务授权
Client从TGS获取票据(client-to-server ticket)。
当client需要申请特定服务时,其向TGS发送以下2条消息:
【消息c】:即消息B的内容(TGS's secret key加密后的TGT),和想获取的服务的服务ID(注意:不是用户ID)。
【消息d】:认证符(Authenticator)(Authenticator包括:用户ID,时间戳),通过Client/TGS会话密钥(Client/TGS Session Key)进行加密。
收到消息c和消息d后,TGS首先检查KDC数据库中是否存在所需的服务,查找到之后,TGS用自己的“TGS密钥”(TGS's secret key)解密消息c中的消息B(也就是TGT),从而得到之前生成的“Client/TGS会话密钥”(Client/TGS Session Key)。TGS再用这个Session Key解密消息d得到包含用户ID和时间戳的Authenticator,并对TGT和Authenticator进行验证,验证通过之后返回2条消息:
【消息E】:client-server票据(client-to-server ticket)(该ticket包括:Client/SS会话密钥 (Client/Server Session Key),用户ID,用户网址,有效期),通过提供该服务的服务器密钥(service's secret key) 进行加密。
【消息F】:Client/SS会话密钥( Client/Server Session Key) (该Session Key用在将来Client与Server Service的通信(会话)上),通过Client/TGS会话密钥(Client/TGS Session Key) 进行加密。
Client收到这些消息后,用“Client/TGS会话密钥”(Client/TGS Session Key)解密消息F,得到“Client/SS会话密钥”(Client/Server Session Key)。(注意:Client不能解密消息E,因为E是用“服务器密钥”(service's secret key)加密的)。
2.2.3 服务请求
Client从SS获取服务。
当获得“Client/SS会话密钥”(Client/Server Session Key)之后,Client就能够使用服务器提供的服务了。Client向指定服务器SS发出2条消息:
【消息e】:即上一步中的消息E“client-server票据”(client-to-server ticket),通过服务器密钥(service's secret key) 进行加密
【消息g】:新的Authenticator(包括:用户ID,时间戳),通过Client/SS会话密钥(Client/Server Session Key) 进行加密
SS用自己的密钥(service's secret key)解密消息e从而得到TGS提供的Client/SS会话密钥(Client/Server Session Key)。再用这个会话密钥解密消息g得到Authenticator,(同TGS一样)对Ticket和Authenticator进行验证,验证通过则返回1条消息(确认函:确证身份真实,乐于提供服务)。
【消息H】:新时间戳(新时间戳是:Client发送的时间戳加1,v5已经取消这一做法),通过Client/SS会话密钥(Client/Server Session Key) 进行加密。
Client通过Client/SS会话密钥(Client/Server Session Key)解密消息H,得到新时间戳并验证其是否正确。验证通过的话则客户端可以信赖服务器,并向服务器(SS)发送服务请求。
服务器(SS)向客户端提供相应的服务。
3.Kerberos HA架构
Kerberos支持两种服务器在域内冗余方式:Master/Slave(MIT和Heimdal)和Multimaster结构(Windows Active Directory)。在生产环境中部署Kerberos时,最好使用一主(Master)多从(Slave)的架构,以确保Kerberos服务的高可用性。
Kerberos中每个KDC都包含数据库的副本。主KDC包含域(Realm)数据库的可写副本,它以固定的时间间隔复制到从KDC中。所有数据库更改(例如密码更改)都在主KDC上进行,当主KDC不可用时,从KDC提供Kerberos票据给服务授权,但不提供数据库管理。KDC需要一个Admin来进行日常的管理操作。
Kerberos的同步机制只复制主数据库的内容,但不传递配置文件,以下文件必须手动复制到每个Slave中:
- krb5.conf
- kdc.conf
- kadm5.acl
- master key stash file
3.1 HA方案
目前单机房HA方案使用的较多的是Keepalived + Rsync 。Keepalived可以将多个无状态的单点通过虚拟IP(以下称为VIP)漂移的方式搭建成一个高可用服务。
首先,在Master KDC中创建数据库的dump文件(将当前的Kerberos和KADM5数据库转储为ASCII文件):
kdb5_util dump [-b7|-ov|-r13] [-verbose] [-mkey_convert] [-new_mkey_file mkey_file] [-rev] [-recurse] [filename [principals...]]
然后使用Rsync将目录同步到Slave机器的对应目录中,再导入KDC中:
kdb5_util load [-b7|-ov|-r13] [-hash] [-verbose] [-update] filename [dbname]
Hadoop所有请求通过请求内网域名,解析到Keepalived绑定的VIP的方式来使用KDC:
4. 优化和展望
4.1 优化
(1)用户(Principal)管理
如果团队中已经有一套权限系统,要将现有的身份系统集成到Kerberos中会很困难。随着业务的飞速增长,服务器规模越来越大,Kerberos Principal手动操作会越来越频繁,手动的增删改查维护会非常痛苦。需要在Kerberos管理系统中规范Principal申请、维护、删除、keytab生成流程。Principal申请和权限管理自动化。
(2)数据同步优化
Kerberos数据同步可以将生成的数据记录同步写入到MySQL中,使用MySQL双主同步方式。在跨机房环境中,KDC数据使用Rsync工具进行增量同步。以A核心机房作为主机房,Rsync Server使用了Keepalived VIP的方式,当Kerberos主机宕机后,VIP漂移到另外一台主机器上,Rsync Client会以VIP所在的KDC主机器为Rsync Server进行数据同步,以保证KDC数据同步的高可用性。
(3)运维
使用进程管理工具对Kerberos相关进程进行存活监控,当发现有进程异常退出时,邮件/微信/钉钉报警,主动再次拉起进程。
4.2 展望
部署过Kerberos的同学都知道,在Hadoop集群部署Kerberos实际是一项非常繁琐的工作。Kerberos本质上是一种协议或安全通道,对于大多数用户或普通用户来说,是有一定学习曲线的,是否有更好的实现能够对普通用户隐藏这些繁琐的细节。
阿里和Intel合作项目Hadoop Authentication Service (HAS) 据称目前已经应用到ApsaraDB for HBase2.0中:
HAS方案使用Kerby替代MIT Kerberos服务,利用HAS插件式验证方式建立一套人们习惯的账户密码体系。
目前HAS在Apache Kerby项目has-project分支开发中,未来会作为Kerbby的新feature出现在下一次release中。
Apache Kerby作为Apache Directory的一个子项目,目前关注度并不高,让我们期待它在后续的发展吧。
八幅漫画理解使用 JWT设计的单点登录系统
上次在《JSON Web Token - 在Web应用间安全地传递信息》中我提到了JSON Web Token可以用来设计单点登录系统。我尝试用八幅漫画先让大家理解如何设计正常的用户认证系统,然后再延伸到单点登录系统。如果还没有阅读《JSON Web Token - 在Web应用间安全地传递信息》,我强烈建议你花十分钟阅读它,理解JWT的生成过程和原理。用户认证八步走所谓用户认证(Authentication),就是让用户登录,并且在接下来的一段时间内让用户访问网站时可以使用其账户,而不需要再次登录的机制。小知识:可别把用户认证和用户授权(Authorization)搞混了。用户授权指的是规定并允许用户使用自己的权限,例如发布帖子、管理站点等。首先,服务器应用(下面简称“应用”)让用户通过Web表单将自己的用户名和密码发送到服务器的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。接下来,应用和数据库核对用户名和密码。核对用户名和密码成功后,应用将用户的id(图中的user_id)作为JWT Payload的一个属性,将其与头部分别进行Base64编码拼接后签名,形成一个JWT。这里的JWT就是一个形同lll.zzz.xxx的字符串。应用将JWT字符串作为该请求Cookie的一部分返回给用户。注意,在这里必须使用HttpOnly属性来防止Cookie被JavaScript读取,从而避免跨站脚本攻击(XSS攻击)。在Cookie失效或者被删除前,用户每次访问应用,应用都会接受到含有jwt的Cookie。从而应用就可以将JWT从请求中提取出来。应用通过一系列任务检查JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)。应用在确认JWT有效之后,JWT进行Base64解码(可能在上一步中已经完成),然后在Payload中读取用户的id值,也就是user_id属性。这里用户的id为1025。应用从数据库取到id为1025的用户的信息,加载到内存中,进行ORM之类的一系列底层逻辑初始化。应用根据用户请求进行响应。和Session方式存储id的差异Session方式存储用户id的最大弊病在于要占用大量服务器内存,对于较大型应用而言可能还要保存许多的状态。一般而言,大型应用还需要借助一些KV数据库和一系列缓存机制来实现Session的存储。而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。除了用户id之外,还可以存储其他的和用户相关的信息,例如该用户是否是管理员、用户所在的分桶(见[《你所应该知道的A/B测试基础》一文](/2015/08/27/introduction-to-ab-testing/)等。虽说JWT方式让服务器有一些计算压力(例如加密、编码和解码),但是这些压力相比磁盘I/O而言或许是半斤八两。具体是否采用,需要在不同场景下用数据说话。单点登录Session方式来存储用户id,一开始用户的Session只会存储在一台服务器上。对于有多个子域名的站点,每个子域名至少会对应一台不同的服务器,例如:www.taobao.comnv.taobao.comnz.taobao.comlogin.taobao.com所以如果要实现在login.taobao.com登录后,在其他的子域名下依然可以取到Session,这要求我们在多台服务器上同步Session。使用JWT的方式则没有这个问题的存在,因为用户的状态已经被传送到了客户端。因此,我们只需要将含有JWT的Cookie的domain设置为顶级域名即可,例如Set-Cookie: jwt=lll.zzz.xxx; HttpOnly; max-age=980000; domain=.taobao.com注意domain必须设置为一个点加顶级域名,即.taobao.com。这样,taobao.com和*.taobao.com就都可以接受到这个Cookie,并获取JWT了。
Cookie、Session、Token与JWT解析
认证、授权与凭证什么是认证(Authentication)?通俗地讲就是验证当前用户的身份是否合法的过程,即你是谁?证明“你是你自己”(比如:你每天上下班打卡,都需要通过指纹打卡,当你的指纹和系统里录入的指纹相匹配时,就打卡成功)互联网中的认证:用户名密码登录;邮箱发送登录链接;手机号接收验证码。只要你能收到邮箱/验证码,就默认你是账号的主人!认证主要是为了保护系统的隐私数据与资源。拓展:什么是会话?认证通过后,为了避免用户每次操作都进行认证(除银行转账等),可以将用户信息保存在会话中,会话就是系统为了保存当前用户的登录状态所提供的机制,常见的有基于Session和token的方式,具体见下文。什么是授权(Authorization)?简单来讲就是谁(who)对什么(what)进行了什么操作(how)。认证是保证用户的合法性,授权则是为了更细粒度的对隐私数据的划分。*授权是在认证通过后,控制不同的用户访问不同的资源。用户授予第三方应用访问该用户某些资源的权限。比如,你在安装手机应用的时候,APP 会询问是否允许授予权限(访问相册、地理位置等权限);你在访问微信小程序时,当登录时,小程序会询问是否允许授予权限(获取昵称、头像、地区、性别等个人信息)实现授权的方式有:业界通常基于R(role/resource)BAC实现授权:(1)基于角色的访问控制(2)基于资源(权限)的访问控制,系统设计时定义好某项操作的权限标识,系统扩展性好。什么是凭证(Credentials)实现认证和授权的前提是需要一种媒介(证书) 来标记访问者的身份。例如:在战国时期,商鞅变法,发明了照身帖。照身帖由官府发放,是一块打磨光滑细密的竹板,上面刻有持有人的头像和籍贯信息。国人必须持有,如若没有就被认为是黑户,或者间谍之类的。在现实生活中,每个人都会有一张专属的居民身份证,是用于证明持有人身份的一种法定证件。通过身份证,我们可以办理手机卡/银行卡/个人贷款/交通出行等等,这就是认证的凭证。在互联网应用中,一般网站会有两种模式,游客模式和登录模式。游客模式下,可以正常浏览网站上面的文章,一旦想要点赞/收藏/分享文章,就需要登录或者注册账号。当用户登录成功后,服务器会给该用户使用的浏览器颁发一个令牌(token),这个令牌用来表明你的身份,每次浏览器发送请求时会带上这个令牌,就可以使用游客模式下无法使用的功能。Cookie与Session什么是Cookie?HTTP 是无状态的协议(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息):每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。所以服务器与浏览器为了进行会话跟踪(知道是谁在访问我),就必须主动的去维护一个状态,这个状态用于告知服务端前后两个请求是否来自同一浏览器。而这个状态需要通过 cookie 或者 session 去实现。cookie 存储在客户端: cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。cookie 是不可跨域的: 每个 cookie 都会绑定单一的域名,无法在别的域名下获取使用,一级域名和二级域名之间是允许共享使用的(靠的是 domain)cookie 重要的属性什么是 Sessionsession 是另一种记录服务器和客户端会话状态的机制,即告诉服务端前后两个请求是否来自同一个客户端(浏览器),知道谁在访问我。因为http本身是无状态协议,这样,无法确定你的本次请求和上次请求是不是你发送的。如果要进行类似论坛登陆相关的操作,就实现不了了。session 是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中。ps:还有一种是浏览器禁用了cookie或不支持cookie,这种可以通过URL重写的方式发到服务器;session 认证流程(如上图):用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。根据以上流程可知,SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。基于Session的认证机制由Servlet规范定制,Servlet容器已经实现,用户通过HttpSession的操作方法可以实现:Cookie 和 Session 的区别安全性:Session 是存储在服务器端的,Cookie 是存储在客户端的。所以 Session 相比 Cookie 安全,存取值的类型不同:Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型。有效期不同: Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。存储大小不同: 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。拓展:Session痛点看起来通过 cookie + session 的方式是解决了问题, 但是我们忽略了一个问题,上述情况能正常工作是因为我们假设 server 是单机工作的,但实际在生产上,为了保障高可用,一般服务器至少需要两台机器,通过负载均衡的方式来决定到底请求该打到哪台机器上。假设登录请求打到了 A 机器,A 机器生成了 session 并在 cookie 里添加 sessionId 返回给了浏览器,那么问题来了:下次添加购物车时如果请求打到了 B 或者 C,由于 session 是在 A 机器生成的,此时的 B,C 是找不到 session 的,那么就会发生无法添加购物车的错误,就得重新登录了,此时请问该怎么办。主要有以下三种方式:(1 )session 复制A 生成 session 后复制到 B, C,这样每台机器都有一份 session,无论添加购物车的请求打到哪台机器,由于 session 都能找到,故不会有问题这种方式虽然可行,但缺点也很明显:同一样的一份 session 保存了多份,数据冗余如果节点少还好,但如果节点多的话,特别是像阿里,微信这种由于 DAU 上亿,可能需要部署成千上万台机器,这样节点增多复制造成的性能消耗也会很大。(2)session 粘连这种方式是让每个客户端请求只打到固定的一台机器上,比如浏览器登录请求打到 A 机器后,后续所有的添加购物车请求也都打到 A 机器上,Nginx 的 sticky 模块可以支持这种方式,支持按 ip 或 cookie 粘连等等,如按 ip 粘连方式如下这样的话每个 client 请求到达 Nginx 后,只要它的 ip 不变,根据 ip hash 算出来的值会打到固定的机器上,也就不存在 session 找不到的问题了,当然不难看出这种方式缺点也是很明显,对应的机器挂了怎么办?(3)session 共享这种方式也是目前各大公司普遍采用的方案,将 session 保存在 redis,memcached 等中间件中,请求到来时,各个机器去这些中间件取一下 session 即可。缺点其实也不难发现,就是每个请求都要去 redis 取一下 session,多了一次内部连接,消耗了一点性能,另外为了保证 redis 的高可用,必须做集群,当然了对于大公司来说, redis 集群基本都会部署,所以这方案可以说是大公司的首选了。Token(令牌)与 JWT(跨域认证)Token概述(no session!)通过上文分析我们知道通过在服务端共享 session 的方式可以完成用户的身份定位,但是不难发现也有一个小小的瑕疵:搞个校验机制我还得搭个 redis 集群?大厂确实 redis 用得比较普遍,但对于小厂来说可能它的业务量还未达到用 redis 的程度,所以有没有其他不用 server 存储 session 的用户身份校验机制呢,使用token!简单来说:首先请求方输入自己的用户名,密码,然后 server 据此生成 token,客户端拿到 token 后会保存到本地(token存储在浏览器端),之后向 server 请求时在请求头带上此 token 即可(server有校验机制,检验token合法性,同时server通过token中携带的uid确定是谁在访问它)。可以看到 token 主要由三部分组成:header:指定了签名算法payload:可以指定用户 id,过期时间等非敏感数据Signature: 签名,server 根据 header 知道它该用哪种签名算法,再用密钥根据此签名算法对 head + payload 生成签名,这样一个 token 就生成了。当 server 收到浏览器传过来的 token 时,它会首先取出 token 中的 header + payload,根据密钥生成签名,然后再与 token 中的签名比对,如果成功则说明签名是合法的,即 token 是合法的。而且你会发现 payload 中存有我们的 userId,所以拿到 token 后直接在 payload 中就可获取 userid,避免了像 session 那样要从 redis 去取的开销。你会发现这种方式确实很妙,只要 server 保证密钥不泄露,那么生成的 token 就是安全的,因为如果伪造 token 的话在签名验证环节是无法通过的,就此即可判定 token 非法。可以看到通过这种方式有效地避免了 token 必须保存在 server 的弊端,实现了分布式存储,不过需要注意的是,token 一旦由 server 生成,它就是有效的,直到过期,无法让 token 失效,除非在 server 为 token 设立一个黑名单,在校验 token 前先过一遍此黑名单,如果在黑名单里则此 token 失效,但一旦这样做的话,那就意味着黑名单就必须保存在 server,这又回到了 session 的模式,那直接用 session 不香吗。所以一般的做法是当客户端登出要让 token 失效时,直接在本地移除 token 即可,下次登录重新生成 token 就好。另外需要注意的是 token 一般是放在 header 的 Authorization 自定义头里,不是放在 Cookie 里的,这主要是为了解决跨域不能共享 Cookie 的问题总结:token解决什么问题(为什么要用token)?完全由应用管理,可以避开同源策略支持跨域访问,cookie不支持, Cookie 跨站是不能共享的,这样的话如果你要实现多应用(多系统)的单点登录(SSO),使用 Cookie 来做需要的话就很困难了。但如果用 token 来实现 SSO 会非常简单,只要在 header 中的 authorize 字段(或其他自定义)加上 token 即可完成所有跨域站点的认证。token是无状态的,可以在多个服务器间共享token可以避免CSRF攻击(跨站请求攻击)易于扩展,在移动端原生请求是没有 cookie 之说的,而 sessionid 依赖于 cookie,sessionid 就不能用 cookie 来传了,如果用 token 的话,由于它是随着 header 的 authoriize 传过来的,也就不存在此问题,换句话说token 天生支持移动平台,可扩展性好拓展1:那啥是CSRF呢?攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过(cookie 里带来 sessionId 等身份认证的信息),所以被访问的网站会认为是真正的用户操作而去运行。那么如果正常的用户误点了上面这张图片,由于相同域名的请求会自动带上 cookie,而 cookie 里带有正常登录用户的 sessionid,类似上面这样的转账操作在 server 就会成功,会造成极大的安全风险CSRF 攻击的根本原因在于对于同样域名的每个请求来说,它的 cookie 都会被自动带上,这个是浏览器的机制决定的!至于完成一次CSRF攻击必要的两个步骤:1、首先登了一个正常的网站A,并且在本地生成了cookie2、在cookie有效时间内,访问了危险网站B(就获取了身份信息)Q:那我不访问危险网站就完了呗?A:危险网站也许只是个存在漏洞的可信任网站!Q:那我访问完正常网站,关了浏览器就好了呀?A:即使关闭浏览器,cookie也不保证一定立即失效,而且关闭浏览器并不能结束会话,session的生命周期跟这些都没关系。拓展2:同源策略?就是不同源的客户端脚本在没有明确授权情况下,不准读写对方的资源!同源就是:协议、域名与端口号都相同。同源策略是由 Netscape 提出的著名安全策略,是浏览器最核心、基本的安全功能,它限制了一个源中加载脚本与来自其他源中资源的交互方式。拓展3:什么是跨域,如何解决?当浏览器执行脚本时会检查是否同源,只有同源的脚本才会执行,如果不同源即为跨域。产生原因:它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。解决方案:nginx(静态服务器)反向代理解决跨域(前端常用),a明确访问c代理服务器,但是不知道c的内容从哪里来,c反向从别的地方拿来数据。(忽略的是目标地址),浏览器可以访问a,而服务器之间不存在跨域问题,浏览器先访问a的服务器c,让c服务器作为代理去访问b服务器,拿到之后再返回数据给a。jsonp:通常为了减轻web服务器的负载,我们把js、css、图片等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许。添加响应头拓展4:易于扩展?比如有多台服务器,使用负载均衡,第一次登录转发到了A,A中seesion缓存了用户的登录信息,第二次登录转发到了B,这时候就丢失了登录状态,当然这样也是有解决方案可以共享session,但token只需要所有的服务器使用相同的解密手段即可。拓展5:无状态?服务端不保存客户端请求者的任何信息,客户端每次请求必须自备描述信息,通过这些信息来识别客户端身份。服务端只需要确认该token是否是自己亲自签发即可,签发和验证都在服务端进行。拓展6:什么是单点登录?所谓单点登录,是指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。Acesss Token访问资源接口(API)时所需要的资源凭证简单 token 的组成: uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)特点:服务端无状态化、可扩展性好支持移动端设备安全性高支持跨程序调用token 的身份验证流程:客户端使用用户名跟密码请求登录服务端收到请求,去验证用户名与密码验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里客户端每次向服务端请求资源的时候需要带着服务端签发的 token服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据注意点:每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用解析 token 的计算时间换取 session 的存储空间,从而减轻服务器的压力,减少频繁的查询数据库token 完全由应用管理,所以它可以避开同源策略Refresh Token另外一种 token——refresh tokenrefresh token 是专用于刷新 access token 的 token。如果没有 refresh token,也可以刷新 access token,但每次刷新都要用户输入登录用户名与密码,会很麻烦。有了 refresh token,可以减少这个麻烦,客户端直接用 refresh token 去更新 access token,无需用户进行额外的操作。两者区别:Access Token 的有效期比较短,当 Acesss Token 由于过期而失效时,使用 Refresh Token 就可以获取到新的 Token,如果 Refresh Token 也失效了,用户就只能重新登录了。Refresh Token 及过期时间是存储在服务器的数据库中,只有在申请新的 Acesss Token 时才会验证,不会对业务接口响应时间造成影响,也不需要向 Session 一样一直保持在内存中以应对大量的请求。Token 的缺点那有人就问了,既然 token 这么好,那为什么各个大公司几乎都采用共享 session 的方式呢,可能很多人是第一次听到 token,token 不香吗? token 有以下两点劣势:token 太长了:token 是 header, payload 编码后的样式,所以一般要比 sessionId 长很多,很有可能超出 cookie 的大小限制(cookie 一般有大小限制的,如 4kb),如果你在 token 中存储的信息越长,那么 token 本身也会越长,这样的话由于你每次请求都会带上 token,对请求来是个不小的负担不太安全:网上很多文章说 token 更安全,其实不然,细心的你可能发现了,我们说 token 是存在浏览器的,再细问,存在浏览器的哪里?既然它太长放在 cookie 里可能导致 cookie 超限,那就只好放在 local storage 里,这样会造成安全隐患,因为 local storage 这类的本地存储是可以被 JS 直接读取的,另外由上文也提到,token 一旦生成无法让其失效,必须等到其过期才行,这样的话如果服务端检测到了一个安全威胁,也无法使相关的 token 失效。所以 token 更适合一次性的命令认证,设置一个比较短的有效期!!!拓展:不管是 cookie 还是 token,从存储角度来看其实都不安全(实际上防护 CSRF 攻击的正确方式是用 CSRF token),都有暴露的风险,我们所说的安全更多的是强调传输中的安全,可以用 HTTPS 协议来传输, 这样的话请求头都能被加密,也就保证了传输中的安全。其实我们把 cookie 和 token 比较本身就不合理,一个是存储方式,一个是验证方式,正确的比较应该是 session vs token。Token 和 Session 的区别token和session其实都是为了身份验证,session一般翻译为会话,而token更多的时候是翻译为令牌;session和token都是有过期时间一说,都需要去管理过期时间;Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息(可能保存在缓存、文件或数据库)。而 Token 是令牌,访问资源接口(API)时所需要的资源凭证。Token 使服务端无状态化,不会存储会话信息。其实token与session的问题是一种时间与空间的博弈问题,session是空间换时间,而token是时间换空间。两者的选择要看具体情况而定。虽然确实都是“客户端记录,每次访问携带”,但 token 很容易设计为自包含的,也就是说,后端不需要记录什么东西,每次一个无状态请求,每次解密验证,每次当场得出合法 /非法的结论。这一切判断依据,除了固化在 CS 两端的一些逻辑之外,整个信息是自包含的。这才是真正的无状态。 而 sessionid ,一般都是一段随机字符串,需要到后端去检索 id 的有效性。万一服务器重启导致内存里的 session 没了呢?万一 redis 服务器挂了呢?所谓 Session 认证只是简单的把 User 信息存储到 Session 里,因为 SessionID 的不可预测性,暂且认为是安全的。而 Token ,如果指的是 OAuth Token 或类似的机制的话,提供的是 认证 和 授权 ,认证是针对用户,授权是针对 App 。其目的是让某 App 有权利访问某用户的信息。这里的 Token 是唯一的。不可以转移到其它 App上,也不可以转到其它用户上。Session 只提供一种简单的认证,即只要有此 SessionID ,即认为有此 User 的全部权利。是需要严格保密的,这个数据应该只保存在站方,不应该共享给其它网站或者第三方 App。所以简单来说:如果你的用户数据可能需要和第三方共享,或者允许第三方调用 API 接口,用 Token 。如果永远只是自己的网站,自己的 App,用什么就无所谓了。JWT概述JSON Web Token(简称 JWT)是目前最流行的跨域认证解决方案。是一种认证授权机制。JWT 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准(RFC 7519)。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上。可以使用 HMAC 算法或者是 RSA 的公/私秘钥对 JWT 进行签名。因为数字签名的存在,这些传递的信息是可信的。阮一峰老师的 JSON Web Token 入门教程 讲的非常通俗易懂,这里就不再班门弄斧了生成 JWThttps://jwt.io/https://www.jsonwebtoken.io/JWT 的原理JWT 认证流程:用户输入用户名/密码登录,服务端认证成功后,会返回给客户端一个 JWT客户端将 token 保存到本地(通常使用 localstorage,也可以使用 cookie)当用户希望访问一个受保护的路由或者资源的时候,需要请求头的 Authorization 字段中使用Bearer 模式添加 JWT,其内容看起来是下面这样Authorization: Bearer <token>服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息,如果合法,则允许用户的行为因为 JWT 是自包含的(内部包含了一些会话信息),因此减少了需要查询数据库的需要因为 JWT 并不使用 Cookie 的,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)因为用户的状态不再存储在服务端的内存中,所以这是一种无状态的认证机制JWT 的使用方式方式一当用户希望访问一个受保护的路由或者资源的时候,可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求头信息的 Authorization 字段里,使用 Bearer 模式添加 JWT。GET /calendar/v1/events用户的状态不会存储在服务端的内存中,这是一种 无状态的认证机制服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息,如果合法,则允许用户的行为。由于 JWT 是自包含的,因此减少了需要查询数据库的需要JWT 的这些特性使得我们可以完全依赖其无状态的特性提供数据 API 服务,甚至是创建一个下载流服务。因为 JWT 并不使用 Cookie ,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)方式二跨域的时候,可以把 JWT 放在 POST 请求的数据体里。方式三通过 URL 传输http://www.example.com/user?token=xxx项目中使用 JWT项目地址:https://github.com/yjdjiayou/jwt-demoToken 和 JWT 的区别相同:都是访问资源的令牌都可以记录用户的信息都是使服务端无状态化都是只有验证成功后,客户端才能访问服务端上受保护的资源区别:Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。JWT:将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。常见的前后端鉴权方式Session-CookieToken 验证(包括 JWT,SSO)OAuth2.0(开放授权)常见的加密算法不可逆加密:【Hash加密算法/散列算法/摘要算法】一旦加密就不能反向解密得到密码原文,一般用来加密用户密码,app的服务器端数据库里一般存储的也都是加密后的用户密码。在数据传输的过程中,首先把密码类数据经过MD5加密算法加密,然后再在外面使用可逆的加密方式加密一次,这样在数据传输的过程中,即便数据被截获了,但是想要完全破解,还是很难的。Hash算法特别的地方在于它是一种单向算法,用户可以通过Hash算法对目标信息生成一段特定长度的唯一的Hash值,却不能通过这个Hash值重新获得目标信息。因此Hash算法常用在不可还原的密码存储、信息完整性校验等。用途:一般用于效验下载文件正确性,一般在网站上下载文件都能见到;存储用户敏感信息,如密码、 卡号等不可解密的信息。常见的不可逆加密算法有:MD5、SHA、HMACMD5:Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。该算法的文件号为RFC 1321(R.Rivest,MIT Laboratory for Computer Science and RSA Data Security Inc. April 1992)。 MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。MD5算法具有以下特点: 1、压缩性:任意长度的数据,算出的MD5值长度都是固定的。 2、容易计算:从原数据计算出MD5值很容易。 3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。 4、强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。 MD5的作用是让大容量信息在用数字签名软件签署私人密钥前被"压缩"成一种保密的格式(就是把一个任意长度的字节串变换成一定长的十六进制数字串)。除了MD5以外,其中比较有名的还有sha-1、RIPEMD以及Haval等。SHA1 :安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准 (Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。 SHA1有如下特点: 1.不可以从消息摘要中复原信息; 2.两个不同的消息不会产生同样的消息摘要,(但会有1x10 ^ 48分之一的机率出现相同的消息摘要,一般使用时忽略)。可逆加密:可逆加密有对称加密和非对称加密。对称加密:【文件加密和解密使用相同的密钥,即加密密钥也可以用作解密密钥】在对称加密算法中,数据发信方将明文和加密密钥一起经过特殊的加密算法处理后,使其变成复杂的加密密文发送出去,收信方收到密文后,若想解读出原文,则需要使用加密时用的密钥以及相同加密算法的逆算法对密文进行解密,才能使其回复成可读明文。在对称加密算法中,使用的密钥只有一个,收发双方都使用这个密钥,这就需要解密方事先知道加密密钥。对称加密算法的优点是算法公开、计算量小、加密速度快、加密效率高。用途:一般用于保存用户手机号、身份证等敏感但能解密的信息。常见的对称加密算法有AES(高级加密标准)、DES(数据加密算法)、3DES、Blowfish、IDEA、RC4、RC5、RC6非对称加密:【两个密钥:公开密钥(publickey)和私有密钥,公有密钥加密,私有密钥解密】非对称加密算法是一种密钥的保密方法。非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将其中的一把作为公用密钥向其它方公开;得到该公用密钥的乙方使用该密钥对机密信息进行加密后再发送给甲方;甲方再用自己保存的另一把专用密钥对加密后的信息进行解密。服务器存私钥,客户端拿公钥,客户端加解密算法可以做成so库。非对称加密与对称加密相比,其安全性更好;非对称加密的缺点是加密和解密花费时间长、速度慢,只适合对少量数据进行加密。用途:一般用于签名和认证。常见的非对称加密算法有:RSA(公钥加密算法)、DSA(数字签名用)、ECC(移动设备用)、Diffie-Hellman、El Gamal常见问题使用 cookie 时需要考虑的问题因为存储在客户端,容易被客户端篡改,使用前需要验证合法性,所以,不要存储敏感数据,比如用户密码,账户余额;使用 httpOnly 在一定程度上提高安全性尽量减少 cookie 的体积,能存储的数据量不能超过 4kb;一个浏览器针对一个网站最多存 20 个Cookie,浏览器一般只允许存放 300 个Cookie设置正确的 domain 和 path,减少数据传输cookie 无法跨域(每个 cookie 都会绑定单一的域名,无法在别的域名下获取使用)移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token使用 session 时需要考虑的问题将 session 存储在服务器里面,当用户同时在线量比较多时,这些 session 会占据较多的内存,需要在服务端定期的去清理过期的 session当网站采用集群部署的时候,会遇到多台 web 服务器之间如何做 session 共享的问题。因为 session 是由单个服务器创建的,但是处理用户请求的服务器不一定是那个创建 session 的服务器,那么该服务器就无法拿到之前已经放入到 session 中的登录凭证之类的信息了。解决方案:写客户端cookie的方式、服务器之间session数据同步、用mysql数据库共享session数据。当多个应用要共享 session 时,除了以上问题,还会遇到跨域问题,因为不同的应用可能部署的主机不一样,需要在各个应用做好 cookie 跨域的处理。sessionId 是存储在 cookie 中的,假如浏览器禁止 cookie 或不支持 cookie 怎么办? 一般会把 sessionId 跟在 url 参数后面即重写 url,所以 session 不一定非得需要靠 cookie 实现移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token使用 token 时需要考虑的问题如果你认为用数据库来存储 token 会导致查询时间太长,可以选择放在内存当中。比如 redis 很适合你对 token 查询的需求。token 完全由应用管理,所以它可以避开同源策略token 可以避免 CSRF 攻击(因为不需要 cookie 了)移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token使用 JWT 时需要考虑的问题因为 JWT 并不依赖 Cookie 的,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。JWT 不加密的情况下,不能将秘密数据写入 JWT。JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。JWT 最大的优势是服务器不再需要存储 Session,使得服务器认证鉴权业务可以方便扩展。但这也是 JWT 最大的缺点:由于服务器不需要存储 Session 状态,因此使用过程中无法废弃某个 Token 或者更改 Token 的权限。也就是说一旦 JWT 签发了,到期之前就会始终有效,除非服务器部署额外的逻辑。JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。JWT 适合一次性的命令认证,颁发一个有效期极短的 JWT,即使暴露了危险也很小,由于每次操作都会生成新的 JWT,因此也没必要保存 JWT,真正实现无状态。为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。使用加密算法时需要考虑的问题绝不要以明文存储密码永远使用 哈希算法 来处理密码,绝不要使用 Base64 或其他编码方式来存储密码,这和以明文存储密码是一样的,使用哈希,而不要使用编码。编码以及加密,都是双向的过程,而密码是保密的,应该只被它的所有者知道, 这个过程必须是单向的。哈希正是用于做这个的,从来没有解哈希这种说法, 但是编码就存在解码,加密就存在解密。绝不要使用弱哈希或已被破解的哈希算法,像 MD5 或 SHA1 ,只使用强密码哈希算法。绝不要以明文形式显示或发送密码,即使是对密码的所有者也应该这样。如果你需要 “忘记密码” 的功能,可以随机生成一个新的 一次性的(这点很重要)密码,然后把这个密码发送给用户。分布式架构下 session 共享方案1. session 复制任何一个服务器上的 session 发生改变(增删改),该节点会把这个 session 的所有内容序列化,然后广播给所有其它节点,不管其他服务器需不需要 session ,以此来保证 session 同步优点: 可容错,各个服务器间 session 能够实时响应。缺点: 会对网络负荷造成一定压力,如果 session 量大的话可能会造成网络堵塞,拖慢服务器性能。2. 粘性 session /IP 绑定策略采用 Ngnix 中的 ip_hash 机制,将某个 ip的所有请求都定向到同一台服务器上,即将用户与服务器绑定。 用户第一次请求时,负载均衡器将用户的请求转发到了 A 服务器上,如果负载均衡器设置了粘性 session 的话,那么用户以后的每次请求都会转发到 A 服务器上,相当于把用户和 A 服务器粘到了一块,这就是粘性 session 机制。优点: 简单,不需要对 session 做任何处理。缺点: 缺乏容错性,如果当前访问的服务器发生故障,用户被转移到第二个服务器上时,他的 session 信息都将失效。适用场景: 发生故障对客户产生的影响较小;服务器发生故障是低概率事件 。实现方式: 以 Nginx 为例,在 upstream 模块配置 ip_hash 属性即可实现粘性 session。3. session 共享(常用)使用分布式缓存方案比如 Memcached 、Redis 来缓存 session,但是要求 Memcached 或 Redis 必须是集群把 session 放到 Redis 中存储,虽然架构上变得复杂,并且需要多访问一次 Redis ,但是这种方案带来的好处也是很大的:实现了 session 共享;可以水平扩展(增加 Redis 服务器);服务器重启 session 不丢失(不过也要注意 session 在 Redis 中的刷新/失效机制);不仅可以跨服务器 session 共享,甚至可以跨平台(例如网页端和 APP 端)4. session 持久化将 session 存储到数据库中,保证 session 的持久化优点: 服务器出现问题,session 不会丢失缺点: 如果网站的访问量很大,把 session 存储到数据库中,会对数据库造成很大压力,还需要增加额外的开销维护数据库。只要关闭浏览器 ,session 真的就消失了?不对。对 session 来说,除非程序通知服务器删除一个 session,否则服务器会一直保留,程序一般都是在用户做 log off 的时候发个指令去删除 session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是大部分 session 机制都使用会话 cookie 来保存 session id,而关闭浏览器后这个 session id 就消失了,再次连接服务器时也就无法找到原来的 session。如果服务器设置的 cookie 被保存在硬盘上,或者使用某种手段改写浏览器发出的 HTTP 请求头,把原来的 session id 发送给服务器,则再次打开浏览器仍然能够打开原来的 session。恰恰是由于关闭浏览器不会导致 session 被删除,迫使服务器为 session 设置了一个失效时间,当距离客户端上一次使用 session 的时间超过这个失效时间时,服务器就认为客户端已经停止了活动,才会把 session 删除以节省存储空间。