-
WebApi.Token安全机制
2018-11-06 14:00:08通过ajax分配相应的clientID和Secret及用户名和密码,后端利用owin进行处理并分配access_token,刷新token调用相应的ajax,根据已提供refresh_token进行刷新;测试页面click_me_please_iframe.html包含相应的刷新和... -
【信息安全】-认证机制中的session和token
2021-03-22 10:03:11访问时会在自己的cookie中携带服务器给分配的session-id,服务器收到后根据这个session-id在自己的缓存中索引用户信息,并将保存的用户数据返回给客户端。 很久后的访问 cookie是有失效的,很久之后再次访问会...密码和证书等常见的身份认证手段仅仅用于登录系统时,当登录完成之后,不可能每次请求都去验证密码和账号,所以登陆成功之后需要有一个对用户透明的机制来进行验证,那就是session(同时session也要有保存用户状态的一个作用)
cookie-session机制
原理
- 第一次访问
- 如果以前从来没有登录过某个网站,http头部中不会携带cookie。浏览器为登录后的用户生成一个session-id,并返回给浏览器,浏览器将这个session-id保存在客户端。
- 后序访问
- 访问时会在自己的cookie中携带服务器给分配的session-id,服务器收到后根据这个session-id在自己的缓存中索引用户信息,并将保存的用户数据返回给客户端。
- 很久后的访问
- cookie是有失效的,很久之后再次访问会重新生成性的session-id使原来的session-id失效。
sessionID最常见的做法是保存在cookie中,除此之外还可以保存在URL中,不过这样不是很安全,目前很多的手机浏览器都不支持cookie,在这种情况下只能保存在URL中。
弊端
-
服务器压力增大:通常session是存储在内存中的,每个用户通过认证之后都会将session数据保存在服务器的内存中,而当用户量增大时,服务器的压力增大。
有一种解决办法是把session放在cookie中加密好,当浏览器访问网页时,带上这个cookie,服务器端只要解密就可以得到用户的session。
-
CSRF跨站伪造请求攻击:session是基于cookie进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击,cookie劫持https://blog.csdn.net/qq_39328436/article/details/114262076
-
扩展性不强:如果将来搭建了多个服务器,虽然每个服务器都执行的是同样的业务逻辑,但是session数据是保存在内存中的(不是共享的),用户第一次访问的是服务器1,当用户再次请求时可能访问的是另外一台服务器2,服务器2获取不到session信息,就判定用户没有登陆过。
token机制
在cookie-session机制中,最大的弊端就是服务器要存储session,token主要就是用来缓解这个矛盾的。
原理
客户端在第一次访问时,服务器给客户端颁发一个令牌(token),下次客户端再次访问时,携带着这个令牌,便完成了一次身份认证。
令牌颁发 令牌校验 token验证的过程:
- 客户端使用用户名跟密码请求登录
- 服务端收到请求,去验证用户名与密码
- 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
- 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
- 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
- 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
session VS token
1.session保存在服务器端,导致服务器压力增大,不利于服务器扩展。token保存在客户端,服务器用计算时间换取了存储空间。
2.token更加安全,请求中发送token而不再是发送cookie能够防止CSRF(跨站请求伪造),因为token会被拼接在url中,难以伪造。
- 第一次访问
-
app开发核心技术《一》之token验签机制(java篇)
2018-08-22 19:18:35一、客户端(APP)和服务端验签机制 1.背景 在以前传统项目中,当用户输入完正确的用户名和密码之后,服务端会在session存入用户的信息,即客户端登录成功后,服务端给其分配一个sessionId,返回给客户端,以后...一、客户端(APP)和服务端验签机制
1.背景
在以前传统项目中,当用户输入完正确的用户名和密码之后,服务端会在session存入用户的信息,即客户端登录成功后,服务端给其分配一个sessionId,返回给客户端,以后每次客户端请求带着sessionId。这种方式进行交互的时候必然存在安全风险,上述登录流程, 我们服务器判断用户的登录状态, 完全依赖于sessionId,一旦其被截获, 黑客就能够模拟出用户的请求。例如(我们产品中以前真实发生过的):现在wifi随处可见,用户通过wifi连接到公网,请求我们服务器,这样在外连接不安全的wifi的时候,黑客就有机可趁了,他通过网络传输工具,在用户登录我们APP之后,拦截获取到我们传给客户端的sessionId;并通过用户在APP中的操作,抓取到一些关键性的请求地址,进行伪造攻击。
基于此,我引入了当时比较流行的解决方案,通过RAS秘钥对加密并引入token机制,防止被恶意拦截攻击。方案如下:
1)客户端在第一次进入的时候会进行初始化,服务端通过RSA生产秘钥对A,私钥A存在服务端,公钥A传给客户端;
2)用户在登录时,将密码用公钥A进行RSA加密,并初始化RSA秘钥对B,将公钥B,用户名,加密后的密码传给服务端。服务端通过私钥A进行解密验证密码,通过后生产唯一字符串token,并通过公钥B进行加密token和session一起传给客户端;
3)客户端通过私钥B进行解密获得真正的token,存在上下文中,以后每次请求,都用公钥A进行加密传给服务端进行验证
这样,就算被拦截,黑客获取到的都是密文,直接用户伪造请求服务端的校验会报错,解密??呵呵(破解这算法要买最好的计算机和花几个月长则几年的时间,取决于你秘钥的长度),难道太大,成本太高,黑客也会放弃攻击的。(就像爬虫和反爬虫,当爬虫成本远大于收益的时候,爬虫者也会放弃爬去该网站(保护成功),反正人家不爬你爬谁)
二、RSA加密(JAVA端实现)
package com.xiatian.scb.demo.util; import org.springframework.util.Base64Utils; import javax.crypto.Cipher; import java.io.ByteArrayOutputStream; import java.security.*; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.HashMap; import java.util.Map; /** * 〈一句话功能简述〉<br> * 〈功能详细描述〉 * * @author 18043622 2018/8/22 * @see [相关类/方法](可选) * @since [产品/模块版本] (可选) */ public class RSAUtils { /** *//** * 加密算法RSA */ public static final String KEY_ALGORITHM = "RSA"; /** *//** * 签名算法 */ public static final String SIGNATURE_ALGORITHM = "MD5withRSA"; /** *//** * 获取公钥的key */ private static final String PUBLIC_KEY = "RSAPublicKey"; /** *//** * 获取私钥的key */ private static final String PRIVATE_KEY = "RSAPrivateKey"; /** *//** * RSA最大加密明文大小 */ private static final int MAX_ENCRYPT_BLOCK = 117; /** *//** * RSA最大解密密文大小 */ private static final int MAX_DECRYPT_BLOCK = 128; /** *//** * <p> * 生成密钥对(公钥和私钥) * </p> * * @return * @throws Exception */ public static Map<String, Object> genKeyPair() throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); keyPairGen.initialize(1024); KeyPair keyPair = keyPairGen.generateKeyPair(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); Map<String, Object> keyMap = new HashMap<>(2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } /** *//** * <p> * 用私钥对信息生成数字签名 * </p> * * @param data 已加密数据 * @param privateKey 私钥(BASE64编码) * * @return * @throws Exception */ public static String sign(byte[] data, String privateKey) throws Exception { byte[] keyBytes = Base64Utils.decodeFromString(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initSign(privateK); signature.update(data); return Base64Utils.encodeToString(signature.sign()); } /** *//** * <p> * 校验数字签名 * </p> * * @param data 已加密数据 * @param publicKey 公钥(BASE64编码) * @param sign 数字签名 * * @return * @throws Exception * */ public static boolean verify(byte[] data, String publicKey, String sign) throws Exception { byte[] keyBytes = Base64Utils.decodeFromString(publicKey); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); PublicKey publicK = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initVerify(publicK); signature.update(data); return signature.verify(Base64Utils.decodeFromString(sign)); } /** *//** * <P> * 私钥解密 * </p> * * @param encryptedData 已加密数据 * @param privateKey 私钥(BASE64编码) * @return * @throws Exception */ public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception { byte[] keyBytes = Base64Utils.decodeFromString(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateK = keyFactory.generatePrivate(pkcs8KeySpec); Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, privateK); int inputLen = encryptedData.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段解密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_DECRYPT_BLOCK) { cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_DECRYPT_BLOCK; } byte[] decryptedData = out.toByteArray(); out.close(); return decryptedData; } /** *//** * <p> * 公钥解密 * </p> * * @param encryptedData 已加密数据 * @param publicKey 公钥(BASE64编码) * @return * @throws Exception */ public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey) throws Exception { byte[] keyBytes = Base64Utils.decodeFromString(publicKey); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key publicK = keyFactory.generatePublic(x509KeySpec); Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, publicK); int inputLen = encryptedData.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段解密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_DECRYPT_BLOCK) { cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_DECRYPT_BLOCK; } byte[] decryptedData = out.toByteArray(); out.close(); return decryptedData; } /** *//** * <p> * 公钥加密 * </p> * * @param data 源数据 * @param publicKey 公钥(BASE64编码) * @return * @throws Exception */ public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception { byte[] keyBytes = Base64Utils.decodeFromString(publicKey); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key publicK = keyFactory.generatePublic(x509KeySpec); // 对数据加密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, publicK); int inputLen = data.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段加密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_ENCRYPT_BLOCK; } byte[] encryptedData = out.toByteArray(); out.close(); return encryptedData; } /** *//** * <p> * 私钥加密 * </p> * * @param data 源数据 * @param privateKey 私钥(BASE64编码) * @return * @throws Exception */ public static byte[] encryptByPrivateKey(byte[] data, String privateKey) throws Exception { byte[] keyBytes = Base64Utils.decodeFromString(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateK = keyFactory.generatePrivate(pkcs8KeySpec); Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, privateK); int inputLen = data.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段加密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_ENCRYPT_BLOCK; } byte[] encryptedData = out.toByteArray(); out.close(); return encryptedData; } /** *//** * <p> * 获取私钥 * </p> * * @param keyMap 密钥对 * @return * @throws Exception */ public static String getPrivateKey(Map<String, Object> keyMap) throws Exception { Key key = (Key) keyMap.get(PRIVATE_KEY); return Base64Utils.encodeToString(key.getEncoded()); } /** *//** * <p> * 获取公钥 * </p> * * @param keyMap 密钥对 * @return * @throws Exception */ public static String getPublicKey(Map<String, Object> keyMap) throws Exception { Key key = (Key) keyMap.get(PUBLIC_KEY); return Base64Utils.encodeToString(key.getEncoded()); } public static void main(String[] args) throws Exception { Map<String, Object> keyMap = RSAUtils.genKeyPair(); // 测试RSA System.err.println("公钥加密——私钥解密"); String source = "我喜欢大灰狼,啦啦啦,大灰狼。密文你看不懂哦"; System.out.println("\r加密前文字:\r\n" + source); byte[] data = source.getBytes(); byte[] encodedData = RSAUtils.encryptByPublicKey(data, RSAUtils.getPublicKey(keyMap)); System.out.println("加密后文字:\r\n" + Base64Utils.encodeToString(encodedData)); byte[] decodedData = RSAUtils.decryptByPrivateKey(encodedData, RSAUtils.getPrivateKey(keyMap)); String target = new String(decodedData); System.out.println("解密后文字: \r\n" + target); } }
输出结果:
加密前文字:
我喜欢大灰狼,啦啦啦,大灰狼。密文你看不懂哦
加密后文字:
Ap7sY2UnWyfKgqq+SgXDEH8u0XwmybDzdC+o4O2QQl+3aEYz5aUJpGJH7qoxQaEEwkEJMGGeKo6rkXlvi6j+2zJ56CBO34dyq9R6bc2eCZgcz21ghclKHYkOCOTkpKUh9g7BkjFIiUgtrrARlVMt1CmTVtEFn+ToyWq+K6/R3ms=
解密后文字:
我喜欢大灰狼,啦啦啦,大灰狼。密文你看不懂哦这样 黑客最多得到密文,他也看不懂的啦
其实,以上token机制为了以防万一(黑客在用户手机上装了流氓软件,解开了token明文)。我们也可以做一个token失效机制,每隔5分钟就更新token一次(失效的token不能通过校验),做个保护。
-
WebApi后端框架Token身份认证,Api接口Token验证
2021-04-05 21:41:51令牌概述(Token) 在以用户账号体系作为安全认证的信息系统中,对用户身份的鉴定是非常重要的事情。 ...令牌机制是软件系统安全体系中非常重要的部分,在计算机身份认证中...Token最大的特点是系统临时分配的一...令牌概述(Token)在以用户账号体系作为安全认证的信息系统中,对用户身份的鉴定是非常重要的事情。令牌机制是软件系统安全体系中非常重要的部分,在计算机身份认证中是令牌的意思,一般作为邀请、登录以及安全认证使用。Token其实说的通俗点就是“暗号”,在一些数据传输之前,要先进行暗号的核对,不同的暗号被授权不同的数据操作。随着互联网行业快速的发展逐渐的演变成了前后端分离,若项目中需要做登录的话,那么token成为前后端唯一的一个凭证。Token最大的特点是系统临时分配的一组数据,可以设定过期时间,可以追踪令牌使用过程,可以通过令牌控制用户禁止或允许使用业务系统。随着移动互联网时代到来,客户端的类型越来越多,逐渐出现了一个服务器对应N个客户端的格局。WebApi服务端与客户端应用下图是CSFramework.WebApi开发框架的前后端应用场景。不同的客户端产生了不同的用户使用场景,有如下几种常用场景:1. 不同的环境安全威胁;2. 不同的会话生存周期;3. 不同的用户权限控制体系;4. 不同级别的接口调用方式;综上所述,它们的身份认证方式也存在一定的区别,本文将使用一定的篇幅对这些场景进行一些分析和梳理工作。Token机制适用的场景在软件设计与开发方面,下面总结了几种应用场景:1. 用户在Winform系统,如C/S架构的客户端登录系统,使用系统服务;2. 用户在web浏览器端登录系统,使用系统服务;3. 用户在手机端(Android/iOS)登录系统,使用系统服务;4. 用户使用开放接口登录系统,调用系统服务;5. 用户在PC处理登录状态时通过手机扫码授权手机登录(使用得比较少);6. 用户在手机处理登录状态进通过手机扫码授权PC进行登录(比较常见);Token类别通过对场景的细分,得到如下不同的认证token类别:1.原始账号密码类别2.用户名和密码3.API应用ID/KEY4.会话ID类别5.浏览器端token6.移动端token7.API应用token8.接口调用类别9.接口访问token10.身份授权类别11.PC和移动端相互授权的tokenToken的安全性和隐私性:Token 不容易被窃取和盗用(Token动态生成并通过对传送频率控制)Token 即使被窃取,产生的影响也是可控的(通过对有效时间控制、用户控制)关于隐私及隐私破坏后的后果,有如下的基本结论:1. 曝光频率高的容易被截获;2. 生存周期长的被截获后对系统安全产生非常大的影响,更严重和不容易察觉;应遵守如下原则杜绝Token被泄露和窃取:1. 变化成本高的token不要轻易变化;2. 不轻易变化的token要减少曝光频率(网络传输次数);3. 曝光频率高的token的生存周期要尽量短;Token量化指标及分类:备注:user_name/password 与 api_key/secret_key 是等价的效果。user_name/password用于内部系统登录。api_key/secret_key用于WebApi架构下的开发者账号和密码。Token层级关系参考上面的分类表,很容易对这些不同用途的token进行分层,主要分为以下4层:密码层:最传统的用户和系统之间约定的数字身份认证方式。会话层:用户登录后的会话生命周期的会话认证。调用层:用户在会话期间对应用程序接口的调用认证。应用层:用户获取了接口访问调用权限后的一些场景或者身份认证应用。Token的分层图如下:在多客户端的软件系统里,Token的产生及应用的内在联系如下:1. 用户输入用户名和用户口令进行一次性认证;2. 在不同的终端里面生成拥有不同生命周期的会话token;3. 客户端会话token从服务端交换生命周期短但曝光 频繁 的接口访问token;4. 会话token可以生成和刷新延长 access_token 的生存时间;5. access_token可以生成生存周期最短的用于授权的二维码的token;密码层:账号密码 (user_name/password)账号/密码是指注册用户名和密码、应用程序的api_key/secret_key。它们的特点有:1. 用户自己为了方便记忆,会设置有一定含义的账号和密码。2. 不常修改,账号密码对用户有特别含义,一般没有特殊情况不会愿意修改。 而api_key/secret_key则会写在应用程序中,修改会意味着重新发布上线的成本3. 一旦泄露影响深远,正因为不常修改,只要泄露了基本相当于用户的网络身份被泄露,而且只要没被察觉这种身份盗用就会一直存在,所以在认证系统中应该尽量减少传输的机会,避免泄露。回话层:客户端会话token充当着session的角色,不同的客户端有不同的生命周期。用户使用账号和密码登录系统,换取会话token。Web平台的Token生存周期短。由于Web登录环境一般很可能是公共环境,被他人盗取的风险值较大。Web平台的客户端主要是指网页,容易被破解源码。移动端生存周期长。移动端平台是个人用户极其私密的平台,它人接触的机会不大。移动端如APP,软件是可以加壳保护源码,不易被破解,因此不易被泄露。调用层:接口存取access_tokenaccess_token是指服务端应用程序api接口访问和调用的凭证。使用具有较长生命周期的会话token来换取此接口访问token。比如使用user_name/password或api_key/secret_key获取access_token。其曝光频率直接和接口调用频率有关,属于高频使用的凭证。为了照顾到隐私性,尽量减少其生命周期,即使被截取了,也不至于产生严重的后果。注意:在客户端token之下还加上一个access_token, 主要是为了让具有不同生命周期的客户端token最后在调用api的时候, 能够具有统一的认证方式。备注:常用系统使用回话层Token即可满足需求,增加一层access_token用于特殊应用环境。应用层:pam_token由已经登录和认证的PC端生成的二维码的原始串号(PC Auth Mobile)。主要步骤如下:1. PC上用户已经完成认证,登录了系统;2. PC端生成一组和此用户相关联的pam_token3. PC端将此pam_token的使用链接生成二维码4. 移动端扫码后,请求服务器,并和用户信息关联5. 移动端获取refresh_token(长时效的会话)6. 根据 refresh_token 获取 access_token7. 完成正常的接口调用工作备注:1. 生存周期为2分钟,2分钟后过期删除2. 没有被使用时,每1分钟变一次3. 被使用后,立刻删除掉4. 此种认证模式一般不会被使用到应用层:map_token由已经登录的移动app来扫码认证PC端系统,并完成PC端系统的登录(Mobile Auth PC)。主要步骤:1. 移动端完成用户身份的认证登录app2. 未登录的PC生成匿名的 map_token3. 移动端扫码后在db中生成 map_token 和用户关联(完成签名)4. db同时针对此用户生成 web_token5. PC端一直以 map_token 为参数查找此命名用户的 web_token6. PC端根据 web_token 去获取 access_token7. 后续正常的调用接口调用工作备注:1. 生存周期为2分钟,2分钟后过期删除2. 没有被使用时,每1分钟变一次3. 被使用后,立刻删除掉在应用中可以适用于且不限于如下场景
2. 有时效的优惠券发放
3. 有时效的邀请码发放
4. 有时效的二维码授权
5. 具有时效 手机/邮件 验证码
6. 多个不同平台调用同一套API接口7. 多个平台使用同一个身份认证中心Token身份认证/Webapi token验证主要是检查当前用户的Token是否有效,包括以下几种状态:1.令牌不存在,2.令牌过期,3.令牌不同,异地用户登录(或相同用户重新登录)验证失败WebApi服务器抛出ResponseException异常。C# Code: /// <summary> /// 检查当前用户的Token: 1.令牌不存在,2.令牌过期,3.令牌不同,异地用户登录(或相同用户重新登录) /// 验证失败抛出ResponseException异常. /// </summary> /// <param name="userID">用户编号,用户系统账号</param> /// <param name="token">用户令牌</param> public static void CheckToken(string userID, string token) { if (String.IsNullOrEmpty(userID) || String.IsNullOrEmpty(token)) throw new ResponseException(ErrorCodes.JsonFormatInvalide, ErrorCodes.JsonFormatInvalide_Msg); //重点!!!必须使用账户判断令牌!!! //返回null表示过期或用户不存在 ModelTokenUser tokenUser = TokenProvider.GetTokenByAccount(userID); //无令牌 if (tokenUser == null) throw new ResponseException(ErrorCodes.TokenExpired, ErrorCodes.TokenExpired_Msg); //令牌不同,异地有用户登录 if (tokenUser.Token != token) throw new ResponseException(ErrorCodes.TokenDuplicateLogin, ErrorCodes.TokenDuplicateLogin_MSG); }
令牌管理器(Token Provider)C# Code: /// <summary> /// 根据系统登录账号获取token /// </summary> /// <param name="account"></param> /// <returns></returns> public static ModelTokenUser GetTokenByAccount(string account) { if (String.IsNullOrEmpty(account)) return null; else { return _data.Where(p => p.Value.Account.ToLower() == account.ToLower()).FirstOrDefault().Value; } }
ModelTokenUser类:
C# Code: /// <summary> /// 用户令牌数据模型 /// </summary> public class ModelTokenUser { /// <summary> /// 用户编号,或APP系统唯一账号(对应手机号码) /// </summary> public string Account { get; set; } /// <summary> /// 令牌 /// </summary> public string Token { get; set; } /// <summary> /// 手机号码 /// </summary> public string Phone { get; set; } /// <summary> /// 客户端信息 /// </summary> public string Client { get; set; } /// <summary> /// 版本 /// </summary> public string Version { get; set; } /// <summary> /// 设备编码 /// </summary> public string MID { get; set; } /// <summary> /// 生成Token时间 /// </summary> public DateTime TS { get; set; } /// <summary> /// 过期时间 /// </summary> public int TokenExpires { get; set; } }
WebApi Token相关参考文章:基于Web前端用户调用CSFramework.WebApi服务端登录登出接口实现CSFramework.WebApi令牌管理器(Token Provider)实现添加、删除、刷新令牌过期控制CSFramework.WebApi后端服务器框架:客户端调用WebApi接口方式(签名+Token令牌)CSFramework.WebApi开发框架模拟Web用户端登录、调用WebApi接口增删改查数据CSFramework.WebApi后端框架Token令牌工作机制以及Token刷新原理WebApi开发框架:Token生成、Token缓存原理、Token验证、令牌机制与原理 -
Add CITY token
2020-12-26 13:05:50它将通过赋能传统企业的方式改变经济价值的产出、流转与分配,以此击碎巨头垄断的经济发展瓶颈,奖励生产关系中的所有贡献者、消费者、服务者等一切有利于价值产生的参与者,从而践行通证经济理念... -
Add sdu token
2020-12-25 23:33:26SDU拥有完善的销毁机制,项目团队将不定期对SDU进行回购销毁,以降低SDU流通总量,推动SDU市价增长。 转账所需 Gas Limit(对 ERC-20 项目方):(默认为 60000) ... -
Add UC token
2020-12-25 23:17:36通过颠覆性的价值认可及价值分配机制,让所有参与者在生态中可直接获得来自对方的价值肯定。以主播和观众为核心,通过去中心化的、可信任的方式让社区参与者的付出和回报是公平的、合理的、不受剥削的。... -
add UHC token
2020-12-25 23:27:31当前医疗卫生资源分配的失衡是导致医疗服务“看病难、看病贵”的问题关键。随着云计算、物联网、移动互联网、大数据等信息化技术的相继成基于区块链的环球医疗健康数字资产去中心化区块链技术熟,互联网医疗在... -
app与后台的token、sessionId、RSA加密登录认证与安全解决方案
2017-09-19 16:45:00一、 登录机制粗略地分析, 登录机制主要分为登录验证、登录保持、登出三个部分。... 登录认保持是指客户端登录后, 服务器能够...容易想到的方案是,客户端登录成功后, 服务器为其分配sessionId, 客户端随后每次请求资一、 登录机制
粗略地分析, 登录机制主要分为登录验证、登录保持、登出三个部分。登录验证是指客户端提供用户名和密码,向服务器提出登录请求,服务器判断客户端是否可以登录并向客户端确认。 登录认保持是指客户端登录后, 服务器能够分辨出已登录的客户端,并为其持续提供登录权限的服务器。登出是指客户端主动退出登录状态。容易想到的方案是,客户端登录成功后, 服务器为其分配sessionId, 客户端随后每次请求资源时都带上sessionId。
1.1 登录验证
上述简易的登录验证策略存在明显的安全漏洞,需要优化。
1.1.1 密码的传输
客户端第一次发出登录请求时, 用户密码以明文的方式传输, 一旦被截获, 后果严重。因此密码需要加密,例如可采用RSA非对称加密。具体流程如下:
客户端向服务器第一次发起登录请求(不传输用户名和密码)。
服务器利用RSA算法产生一对公钥和私钥。并保留私钥, 将公钥发送给客户端。
客户端收到公钥后, 加密用户密码, 向服务器发起第二次登录请求(传输用户名和加密后的密码)。
服务器利用保留的私钥对密文进行解密,得到真正的密码。
1.1.2 登录状态token
再仔细核对上述登录流程, 我们发现服务器判断用户是否登录, 完全依赖于sessionId, 一旦其被截获, 黑客就能够模拟出用户的请求。于是我们需要引入token的概念: 用户登录成功后, 服务器不但为其分配了sessionId, 还分配了token, token是维持登录状态的关键秘密数据。在服务器向客户端发送的token数据,也需要加密。于是一次登录的细节再次扩展。
客户端向服务器第一次发起登录请求(不传输用户名和密码)。
服务器利用RSA算法产生一对公钥和私钥。并保留私钥, 将公钥发送给客户端。
客户端收到公钥后, 加密用户密码,向服务器发送用户名和加密后的用户密码; 同时另外产生一对公钥和私钥,自己保留私钥, 向服务器发送公钥; 于是第二次登录请求传输了用户名和加密后的密码以及客户端生成的公钥。
服务器利用保留的私钥对密文进行解密,得到真正的密码。 经过判断, 确定用户可以登录后,生成sessionId和token,同时利用客户端发送的公钥,对token进行加密。最后将sessionId和加密后的token返还给客户端。
客户端利用自己生成的私钥对token密文解密, 得到真正的token。
1.2 登录保持
在最原始的方案中, 登录保持仅仅靠服务器生成的sessionId: 客户端的请求中带上sessionId, 如果服务器的redis中存在这个id,就认为请求来自相应的登录客户端。 但是只要sessionId被截获, 请求就可以为伪造, 存在安全隐患。
引入token后,上述问题便可得到解决。 服务器将token和其它的一些变量, 利用散列加密算法得到签名后,连同sessionId一并发送给服务器; 服务器取出保存于服务器端的token,利用相同的法则生成校验签名, 如果客户端签名与服务器的校验签名一致, 就认为请求来自登录的客户端。
1.3 TOKEN失效
用户登录出系统
失效原理:
在服务器端的redis中删除相应key为session的键值对。二、 散列算法
散列是信息的提炼,通常其长度要比信息小得多,且为一个固定长度。加密性强的散列一定是不可逆的,这就意味着通过散列结果,无法推出任何部分的原始信息。任何输入信息的变化,哪怕仅一位,都将导致散列结果的明显变化,这称之为雪崩效应。散列还应该是防冲突的,即找不出具有相同散列结果的两条信息。具有这些特性的散列结果就可以用于验证信息是否被修改。
散列算法可以用来加密token生成签名, 以便token信息不暴露在网络同时还能验证登录的有效性。2.1 md5
全写: Message Digest Algorithm MD5(中文名为消息摘要算法第五版)
输出: 128bit
MD5算法具有以下特点:
压缩性:任意长度的数据,算出的MD5值长度都是固定的。
容易计算:从原数据计算出MD5值很容易。
抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
弱抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
强抗碰撞:想找到两个不同的数据,使它们具有相同的MD5值,是非常困难的。
缺陷:
Md5一度被认为十分靠谱。
2004年8月17日的美国加州圣巴巴拉的国际密码学会议(Crypto’2004)上,来自中国山东大学的王小云教授做了破译MD5、HAVAL-128、 MD4和RIPEMD算法的报告,公布了MD系列算法的破解结果。
2009年,冯登国、谢涛二人利用差分攻击,将MD5的碰撞算法复杂度从王小云的2^42进一步降低到2^21,极端情况下甚至可以降低至2^10。仅仅2^21的复杂度意味着即便是在2008年的计算机上,也只要几秒便可以找到一对碰撞。
Md5已老, 在安全性要求较高的场合,不建议使用。2.2 sha1
全名: 安全哈希算法(Secure Hash Algorithm)
输出: 160bit2.2.1 与Md5比较
相同点:
因为二者均由MD4导出,SHA-1和MD5彼此很相似。相应的,他们的强度和其他特性也是相似。
不同点:
1. 对强行攻击的安全性:最显著和最重要的区别是SHA-1摘要比MD5摘要长32 位。使用强行技术,产生任何一个报文使其摘要等于给定报摘要的难度对MD5是2^128数量级的操作,而对SHA-1则是2^160数量级的操作。这样,SHA-1对强行攻击有更大的强度。
2. 对密码分析的安全性:由于MD5的设计,易受密码分析的攻击,SHA-1显得不易受这样的攻击。
3. 速度:在相同的硬件上,SHA-1的运行速度比MD5慢2.3 加盐
所谓加盐, 就是在原本需要加密的信息基础上,糅入其它内容salt。签名的生成就是一次加盐。
三、对称加密
本系统使用对称加密对用户密码进行加密以及生成token字符串。
3.1 AuthCode加密
AuthCode是康盛科技发明的加密方式, 开源产品Discuz的密码是用这个算法进行加密。但是有点遗憾,这个函数所有权属于康盛创想,并不能自由使用的。不知使用是否有风险??
3.2 AES加密
高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
四、非对称加密
RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准。RSA的安全基于大数分解的难度。其公钥和私钥是一对大素数(100到200位十进制数或更大)的函数。从一个公钥和密文恢复出明文的难度,等价于分解两个大素数之积(这是公认的数学难题)。
算法描述:
(1)选择一对不同的、足够大的素数p,q。
(2)计算n=pq。
(3)计算f(n)=(p-1)(q-1),同时对p, q严加保密,不让任何人知道。
(4)找一个与f(n)互质的数e(公钥指数),且1 -
疫情期间,老板给我奖励6683800000个token,我该要吗?
2020-04-28 17:18:39比如:公司疫情经营困难,为了生存下去,不得不降薪,但为了画给大家希望,于是,老板说:未来公司的15%利润将用来发放token激励,根据每个人持有的token数量来分配分红。 token可以允许转让,不需要实名制,也不... -
loadrunner脚本之关联token代码实例演示
2020-06-11 16:21:351.session session的作用:服务器要知道当前发请求给自己的是谁, 为了做这种区分,服务器就给...token的意思就是令牌: 1.客户端使用用户名和密码请求登录 2.登录成功之后,点击投标 3.生成一个token信息 4.后面输入金 -
SeesionId ,Token以及公私钥,服务端与客户端自己的交互
2018-02-07 15:16:55一、登录机制 粗略地分析, 登录机制主要分为登录验证、登录保持、登出三个部分。... 登录认保持是指客户端登录后, 服务器能够分辨出...容易想到的方案是,客户端登录成功后, 服务器为其分配sessionId, 客户端随后每 -
后台的token、sessionId、RSA加密登录认证与安全解决方案
2016-08-30 16:31:55 一、登录机制 粗略地分析, 登录机制主要分为登录验证、登录保持、登出三个部分。... 登录认保持是指客户端登录后, 服务器能够分辨出已...容易想到的方案是,客户端登录成功后, 服务器为其分配sessionId, 客户 -
阿里云临时授权访问STStoken获取
2020-05-09 01:09:22阿里云权限管理机制包括访问控制(Resource Access Management,简称 RAM)和安全凭证管理(Security Token Service,简称 STS),可以根据需求使用不同权限的子账号来访问表格存储,也支持为用户提供访问的临时授权... -
26_iOS干货18之网络3_token & cookie & session & 签名
2018-08-21 23:34:58后都是给⽤用户⾃自⼰己认证的 token,当然也会在登录时重新分配 token ,作为⽤用户的唯 ⼀一凭证 token 值获取: 当⽤用户⾸首次登录成功之后, 服务器器端就会⽣生成⼀一个 token 值. 1.服务器器会将 token 值保存... -
拿来主义之_app与后台的token、sessionId、RSA加密登录认证与安全解决方案
2020-08-26 17:22:03一、 登录机制 ...容易想到的方案是,客户端登录成功后, 服务器为其分配sessionId, 客户端随后每次请求资源时都带上sessionId。 1.1 登录验证 上述简易的登录验证策略存在明显的安全漏洞,需要优化。 . -
linux的qos机制 - dm-ioband篇 (3)
2012-03-05 23:09:22这一篇讲解 ioband 的机制 ioband的原理很简单:...ioband设备基于token来进行控制,根据group的权重分配不同的token。而策略也包括基于request的和基于sector的token控制 dm-ioband涉及到几个重要的数据结构: st -
java 按步骤执行模式_深入理解JVM_java代码的执行机制01
2021-03-12 19:20:53jvm多线程:线程资源同步机制和线程之间交互的机制。3.1 java代码的执行机制java源码编译机制。1、三个步骤:分析和输入到符号表(Parse and Enter)Parse过程所做的为词法和语法分析。词法分析:将代码字符串转变为... -
day21:session认证与token认证
2019-11-17 12:58:34**session认证:**是一种传统的鉴权机制,session是一种对象,它存储在客户端会话所需属性等信息。 认证过程: 客户端第一次向服务器发起登录请求时,服务器会为第一个session对象分配一个唯一的sessionid,并通过... -
联盟链落地与激励机制
2020-08-10 19:34:20联盟链具有效率及可扩展性高的优势,但是需要建立良好的利益分配机制才能吸引企业加入 建议联盟链以双层治理架构组成 : 底层是基于股权的合资公司,上层是基于 Token 的合作联盟。 区块链虽然被视为去中心化的创新...