与授权服务器的对接方案
来源:互联网 发布:淘宝网首页全屏代码 编辑:程序博客网 时间:2024/06/01 11:26
任务背景
之前的项目已经上线了,对于客户有点数控制要求,因此要接入公司的授权服务,进行客户端登录点数的控制。
公司的授权服务主要是针对客户端应用程序的,针对Web应用程序的授权控制需要在服务器端进行操作,与客户端的控制还是有一些区别的。
方案概述
- 授权服务器端进行具体的授权管理,按照应用进行区分,各个应用分配一定的点数进行控制(点数达到上限后则不许登录或者不许启动);
- 按照一定的时间向授权服务器发送心跳,超时之后该用户对应的点数将被释放;
- 用户登出的时候释放所占用的点数;
- 应用与授权服务器需要约定好应用标识符,用以唯一标识该应用;
- 应用(包括Web应用的服务器)与授权服务器之间的交互信息需要加密;
相关技术实现细节
Web端使用的用户标识符
不同于单机的exe产品,可以方便地读取PC的硬件信息,然后使用硬件标识作为用户的唯一标识,以确定该用户(或者该PC)占用了一个点数。
对于Web端产品来讲,JS无法方便地读取硬件信息(好像仅有IE可以通过ActiveX插件来进行读取),所以需要考虑一个替代方案。
此时就想到了利用Cookie:
- 用户登录到服务器的时候,随机生成一个UUID作为该用户的标识符;
- 将该UUID写入到用户的浏览器Cookie中;
- 之后的每次请求以及心跳都携带该Cookie信息上传到服务器,就可以标识该用户了;
这样做会有一个弊端:
同一个用户在同一台电脑上使用多个浏览器的时候,该用户将占用多个点数。不过这样也是合理的。
应用服务器与授权服务器交互的加密秘钥
应用服务器与授权服务器之间的交互需要进行加密处理。
采用的加密算法是AES对称加密,即加密/解密的秘钥是一致的。而这个秘钥并没有提前约定,而是每次通讯的时候临时约定(也就是秘钥信息会包含在请求中)。
由于应用服务器与授权服务器之间的通讯是由应用服务器主动请求,授权服务器被动响应的,即典型的请求/响应模式。所以对于一次请求/响应,应用服务器需要记住秘钥,而授权服务器可以用完就扔。
而出于安全考虑,秘钥确定采用了一个小技巧:
- 授权服务器提前生成一个UUID,执行MD5处理后将该UUID的前16位截取出来,与应用服务器提前约定好作为秘钥的前16位;
- 应用服务器每次请求的时候,随机生成一个UUID并做MD5处理后,作为请求参数的一部分发送请求;
- 而双方实际使用的秘钥是提前约定的UUID&MD5的前16位,加每次随机生成的UUID&MD5的前16位;
这样也保证了传输过程中的秘钥安全性,也保证了每次加解密秘钥的随机性。
加解密&Cipher使用
使用Cipher类来进行相关的加解密工作,此时有一个坑需要注意一下。就是使用Cipher的时候需要替换JRE中的两个包 local_policy.jar,US_export_policy.jar。
这两个包可以到Oracle官网下载,替换到JRE的lib\lib\security目录下。
具体的加解密处理代码如下:
private static String encrypt(String key, String content) { byte[] encryptedBytes; try { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes(), "AES"), new SecureRandom()); encryptedBytes = cipher.doFinal(content.getBytes()); return Base64.getEncoder().encodeToString(encryptedBytes); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } return ""; }
private static String decrypt(String key, String content) { byte[] base64Content; byte[] decryptedBytes; try { base64Content = Base64.getDecoder().decode(content); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key.getBytes(), "AES"), new SecureRandom()); decryptedBytes = cipher.doFinal(base64Content); String decryptedStr = new String(decryptedBytes); return decryptedStr; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } return ""; }
md5算法实现
md5是是一种文章摘要算法,此处使用md5算法是为了剔除UUID中非数字字母的字符。
Java的md5实现是通过MessageDigest做到的,具体代码如下:
private static final char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F'};private static String md5(String info) { MessageDigest digest; try { digest = MessageDigest.getInstance("md5"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return ""; } digest.update(info.getBytes()); byte[] infoBytes = digest.digest(); char infoChars[] = new char[infoBytes.length * 2]; int k = 0; for (byte b : infoBytes) { infoChars[k++] = hexDigits[b >>> 4 & 0xf]; infoChars[k++] = hexDigits[b & 0xf]; } return new String(infoChars); }
服务器交互
服务器交互是使用HttpClient的post方法进行交互的。
参数传递直接传递加密字符串,而不是键值对。也就是说HttpPost的setEntity参数传递的是String,而不是List。
Okay,如上!
- 与授权服务器的对接方案
- 移动端App与后台服务的对接方案
- SpringMvc 文件上传并且实现与ftp服务器的对接
- 服务器socket与安卓端socket对接
- app与服务器对接的http 协议与socket协议区别
- JSTL与SELECT的对接
- action与表单的对接
- 使用信锐无线控制器对接营运商portal服务器的原理分析与排错
- PayPal Android SDK的接入和开发、与服务器对接IPN
- 微信公众号与服务器对接验证
- DHCP服务器的授权问题
- ASP.NET应用程序的安全方案--授权
- 基于操作+角色的授权方案
- 不同方案之间的数据对象授权
- Sequoiadb与Spark的对接步骤
- 与后台对接需要注意的事项
- amoeba与J2EE工程的对接
- iOS 推送所需的授权文件测试与打包(php服务器和java服务器)
- opencv中Mat数据类型中data、size、depth、channels、elemsize、rows、cols等属性内涵
- LIS
- SQL语言-----学习随手记20071023
- 冒泡排序优化
- 给出一个2D板和一个单词,找出这个单词是否存在于网格中。 该单词可以由顺序相邻单元的字母构成,其中“相邻”单元是水平或垂直相邻的单元。 相同的字母单元可能不会被多次使用。
- 与授权服务器的对接方案
- JNI 调用外部 so 里面的函数 so插件化 [附源码]
- 什么是存储过程?有什么优点?
- linux与windows系统有什么区别?
- 使用Gson转化实体类时的混淆配置
- matplotlib中legend位置调整
- ssh免密登录
- Nodejs Error: Can't set headers after they are sent错误解决
- diea快捷键及自动补全