官方网站是什么意思/营销型网站的推广方法

官方网站是什么意思,营销型网站的推广方法,2019年云南建设银行招聘网站,wordpress 漏斗式搜索插件传送门 数据安全系列1:开篇 数据安全系列2:单向散列函数概念 数据安全系列3:密码技术概述 什么是认证? 一谈到认证,多数人的反应可能就是"用户认证" 。就是应用系统如何识别用户的身份,直接…

 传送门

数据安全系列1:开篇

数据安全系列2:单向散列函数概念

数据安全系列3:密码技术概述

什么是认证?

一谈到认证,多数人的反应可能就是"用户认证" 。就是应用系统如何识别用户的身份,直接一点就是常说的"登录"功能,这可以说是一个系统中最基本的功能了:

认证(Authentication)、授权(Authorization)和凭证(Credentials)这三项可以说是一个系统中最基础的安全设计了,哪怕是再简陋的信息系统,大概也不可能忽略掉“用户登录”这个功能。

--------------------引自系统如何正确分辨操作用户的真实身份

而"登录"又是所有安全功能中的重中之重:没有经过用户认证的过程,所有的安全设计都这空中楼阁,这就意义着登录其实不是一件简单的事情:不仅仅是校验一下用户名、密码是否正确这么简单,而是一系列围绕认证展开的复杂问题:

  • 账户和权限信息作为一种必须最大限度保障安全和隐私
  • 同时又要兼顾各个系统模块、甚至是系统间共享访问的基础主数据

所以登录场景下的用户,除了一般意义上的真实的人,也可能不是一个真正的人:只要拥有用户名、密码并经过了系统的安全认证,就可以被系统所接受了。比如有些黑客程序,或者所谓的"攻击机器人",其实并不是真正的用户在操作。但是这里讨论的场景中,用户指的一般开发口中的各种应用系统,以及为了安全性而设计的应用身份识别!

应用身份认证

应用身份认证的场景,其实在开发中还是很常见的(可能对于非开发人员来说,倒不常见,因为一般用户操作的是时候以自己为主体的,所以不存在什么应用身份认证)。

API接口对于程序员来说(尤其是后端开发)几乎是每个人都接触过的,不论是开发API接口还是调用API接口都并不陌生。API接口一般是由应用系统开发出来供别的系统来调用,只要符合接口的规范或约定,一般都能调用成功。这里成功要说明一下:

  • 不考虑网络环境,默认是通的
  • 也不保证业务执行成功与否,只考虑是否满足参数、URL、请求方式等

调用API接口如果只满足基本要求就能调用,在安全性上其实是不够的。就好比一个系统如果没有"登录"这种基本的认证,任何人都能访问那不是一个道理吗?

在一般的内网环境里面,因为有防火墙的存在,其实对于应用之间的API接口调用的认证要求,倒并不是很严格。但是以下的一些情况却是不能忽视:

  • 涉及外网业务,提供了对外的API接口调用
  • 涉及敏感操作,比如转账汇款、删除资源的高危操作
  • 涉及集中管理,比如一些开放网关、公共应用平台系统
  • 其它一些暂时没有想到的......

有上面这些场景,系统就不能再"裸奔"了!对于具体怎么设计应用身份认证并没有统一的标准和既定的规范,放之四海皆准。不过还是有一些借鉴模式:

  • 使用Oauth2协议的密码模式
  • 使用消息认证码模式

具体使用Oauth2的密码模式还是消息论证码模式并没有明确的规定,主要看应用场景。如果是上面提到的开放网关、平台类系统,出于安全性及管理的需要,使用Oauht2的密码模式比较合适。如果是开发小型系统,也不用对接什么平台类的系统,要自主开发一套应用身份认证功能,可以采用消认证码模式,接下来可以具体讨论一下如何实现及对比之间的差异!

Oauth2密码模式

对于Oauth2协议前面讨论的足够多了,其中又专门介绍了Oauth2系列4:密码模式,所以不再赘述。

这里再简单画一个示意图来说明应用场景:

  • A系统开发API接口,并到平台系统注册
  • B系统调用API接口 ,也到平台系统注册
  • 平台系统负责管理注册的应用(包括对应的接口等资源),并负责在系统间接口调用时进行身份论证

那应用身份认证这个场景跟密码模式具体有什么关系呢,或者说为什么可以采用密码模式来做API接口调用的控制?这里觉得有必要做一个探讨与解释。我们知道Oauth协议其实是一个授权协议(可参考Oauth2系列1:初识Oauth2):

看一下网站应用微信登录开发指南

从上面的时序图可以看出标准场景Oauth2的流程有真实用户参与,所以为了应对没有没有真实用户参与的情况,比如应用身份认证(一般都是应用间接口调用,比如服务间通过HTTP接口调用),Oauth2制定了密码模式来应对:将应用模拟为"用户",并也向应用颁发"账号-clientID"、"密码-clientSecret",应用通过账号、密码直接获取token来完成身份认证!上面流程就变成了下面这样:

消息认证码

如果说Oauth2的密码模式适用于平台类系统,提供了一种通用、与业务无关的身份认证方式,那么消息认证码就是另外一种相对更底层与业务参数有关的认证方式。关于消息认证码的概念,可以参考数据安全系列3:密码技术概述,那么为什么消息论证码可以达到身份论证的目的呢?再回顾一下消息论证码的过程:

  • 在这样的交互过程中,交互的双方需要共享密钥,也即是前面的对称密钥
  • 要计算MAC值,必须持有共享密钥,没有就无法计算MAC值,消息认证码正是利用此特性来完成所谓的认证的。

除此以外,还需要说明的是这个过程里面还依赖于单向散列函数的不可逆性!

密钥管理

从Oauh2协议可以看出,可以单独做一个注册服务,负责client_id、client_secret的管理,对网关这种这种平台系统是必要的。如果是对接系统很少甚至就一个,只要双方约定好"密钥"就行:比如服务提供方生成一个16位"随机数",并颁发给调用方作为"密钥",这样会更简单:

UUID.randomUUID().toString()

至于密钥的具体生成、传输、存储、管理也是一个很大话题,一般可能会涉及到KMS之类系统,这里就不展开了。

接下来模拟一个接口,看下通过消息认证码如何实现身份认证!假设有一个用户注册接口:

    @PostMapping("register")public void register(@RequestParam("userName") String userName, @RequestParam("email") String email) {}

接受2个参数userName、email:规定只能拥有"密钥"的系统才能调用。

实现-版本1-基本功能

能最直接想到的办法是,检验参数内容是否符合要求:

  • 调用方:将userName、email拼接起来生成消息认证码,并传递给服务方
  • 服务方:接收userName、email,拼接起来生成消息认证码,并与调用方传递的认证码比较
  • 如果一致,表示认证成功,不一致则不允许调用

通过这个分析,接口就要多加一个参数接收消息认证码,比如叫signature或digest:

    @PostMapping("register")public void register(@RequestParam String userName, @RequestParam String email, @RequestParam String signature) {System.out.printf("userName:" + userName + ",email:" + email + ",signature:" + signature);}

这里还有一个问题就是如何生成消息认证码,这里提供一个Hmacsha256方法(可自行选择算法):

public static String genHmacSha256Sign(String message, String secret) {// 初始化密钥,这里使用一个示例密钥(在实际应用中,密钥应该保密)byte[] secretKeyBytes = secret.getBytes(StandardCharsets.UTF_8);SecretKeySpec secretKey = new SecretKeySpec(secretKeyBytes, "HmacSHA256");try {// 获取HMAC-SHA256的Mac实例Mac mac = Mac.getInstance("HmacSHA256");mac.init(secretKey);// 要签名的数据byte[] dataBytes = message.getBytes(StandardCharsets.UTF_8);mac.update(dataBytes);// 执行MAC计算byte[] resultBytes = mac.doFinal();// 编码为Base64字符串return Base64.getEncoder().encodeToString(resultBytes);} catch (Exception e) {throw new RuntimeException(e);}}

好,现在假定约定的密钥是:826270b4-542b-4e48-b48c-856bea6453db

注册的用户名、email分别是:张三、zhangsan@qq.com,客户端计算出来signature:

public static void main(String[] args) {String secret = "826270b4-542b-4e48-b48c-856bea6453db";String userName = "张三", email = "zhangsan@qq.com";String message = userName + email;System.out.println(genHmacSha256Sign(message, secret));}

输出摘要为:uTo95CYO1AchnvRK9uAJ1W+nc2bJo2p1IsOtLOdWpsk= 

服务端的验证逻辑调成为:

@PostMapping("register")public String register(@RequestParam String userName, @RequestParam String email, @RequestParam String signature) throws UnsupportedEncodingException {System.out.printf("userName:" + userName + ",email:" + email + ",signature:" + signature);String message = userName + email;String sha256Sign = URLDecoder.decode(SignUtil.genHmacSha256Sign(message, "826270b4-542b-4e48-b48c-856bea6453db"), StandardCharsets.UTF_8.name());if (signature.equals(sha256Sign)) {return "success";}return "error";// 省略注册业务逻辑}

现在启动一下服务端,通过postman来调用一下:

调用成功,一个最基本的认证功能实现完成了! 

实现-版本2-与业务解耦

上面的方式虽然实现了功能,不过还是会发现还是有一些问题:

  • signature放在业务接口里面
  • 要针对每个接口的参数单独约定好message的拼接规则(比如哪些参数参与认证、拼接顺序)

总之一句话,身份认证与业务接口没有强绑定了,所以最好把身份认证设计成一个通用的功能:

  • 提供一个过滤器,在里面进行身份认证的检验,并且指定拦截的URL
  • 为了统一message的拼接规则,统一规则接口的所有参数都参与拼接

所以约定:

  • 将signature从业务接口里面提出来,入到header中传递
  • 接口的入参统一用RequestBody的json形式接收,不再定义成RequestParam

改定代码,服务接口:

import com.tw.tsm.auth.dto.RegisterDtoReq;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import java.io.UnsupportedEncodingException;@RestController
public class RegisterController {@PostMapping("register")public String register(@RequestBody RegisterDtoReq register) throws UnsupportedEncodingException {System.out.printf("userName:" + register.getUserName() + ",email:" + register.getEmail() + ",signature:" + signature);// 不再业务代码里面进行身份认证了//        String message = userName + email;
//        String sha256Sign = URLDecoder.decode(SignUtil.genHmacSha256Sign(message, "826270b4-542b-4e48-b48c-856bea6453db"), StandardCharsets.UTF_8.name());
//        if (signature.equals(sha256Sign)) {
//            return "success";
//        }
//        return "error";// 省略注册业务逻辑return null;}}@Data
@NoArgsConstructor
@AllArgsConstructor
public class RegisterDtoReq {private String userName;private String email;
}

过滤器:

import com.tw.tsm.base.util.RequestWrapper;
import com.tw.tsm.base.util.SignUtil;
import org.apache.commons.io.IOUtils;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;public class VerityFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {ServletRequest requestWrapper = null;if (request instanceof HttpServletRequest) {requestWrapper = new RequestWrapper((HttpServletRequest) request);}// 在chain.doFiler方法中传递新的request对象if (requestWrapper == null) {chain.doFilter(request, response);} else {verity((HttpServletRequest) requestWrapper);chain.doFilter(requestWrapper, response);}}private void verity(HttpServletRequest requestWrapper) throws IOException {//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。String requestBody = IOUtils.toString(requestWrapper.getInputStream(), StandardCharsets.UTF_8.name()).replaceAll("\r\n", "");System.out.printf(requestBody);String sha256Sign = SignUtil.genHmacSha256Sign(requestBody, "826270b4-542b-4e48-b48c-856bea6453db");String signature = requestWrapper.getHeader("signature");if (signature.equals(sha256Sign)) {return;}throw new IllegalArgumentException("参数异常!");}
}

包装的HttpServletRequest,用于读取Body:

import org.apache.commons.io.IOUtils;import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;public class RequestWrapper extends HttpServletRequestWrapper {private byte[] requestBody;private HttpServletRequest request;public RequestWrapper(HttpServletRequest request) throws IOException {super(request);this.request = request;}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));}@Overridepublic ServletInputStream getInputStream() throws IOException {if (requestBody == null) {ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();IOUtils.copy(request.getInputStream(), byteArrayOutputStream);this.requestBody = byteArrayOutputStream.toByteArray();}final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);return new ServletInputStream() {@Overridepublic int read() throws IOException {return bais.read();}@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener readListener) {}};}}

注册Filter:

@Beanpublic FilterRegistrationBean httpServletRequestReplacedRegistration() {FilterRegistrationBean registration = new FilterRegistrationBean();registration.setFilter(new VerityFilter());registration.addUrlPatterns("/register");registration.addInitParameter("paramName", "paramValue");registration.setName("VerityFilter");registration.setOrder(1);return registration;}

客户端生成signature:

 public static void main(String[] args) {String secret = "826270b4-542b-4e48-b48c-856bea6453db";String userName = "张三", email = "zhangsan@qq.com";
//        String message = userName + email;JSONObject jsonObject = new JSONObject();jsonObject.put("userName", userName);jsonObject.put("email", email);String message = jsonObject.toJSONString();System.out.println(genHmacSha256Sign(message, secret));// System.out.println(genHmacSha256Sign(jsonObject.toString(), secret));}

 现在启动一下服务端,通过postman来调用一下:

header里面也要传参数:

实现-版本3-防重放

经过迭代过的版本,已经将身份认证与业务接口解耦开了,不过这里还有一个安全问题,就是防重放攻击,具体的应对方案也比较成熟:

  • 加时间戳-timestamp。该方法优点是不用额外保存其他信息。缺点是认证双方需要准确的时间同步,同步越好,受攻击的可能性就越小。但当系统很庞大,跨越的区域较广时,要做到精确的时间同步并不是很容易。所以一般会采用在指定时间范围,比如一分钟以内的请求才接受。并且单独使用时间戳,很难完全杜绝重放攻击
  • 加随机数-nonce。该方法优点是认证双方不需要时间同步,双方记住(客户端生成、传递给服务端)使用过的随机数,如发现报文中有以前使用过的随机数,就认为是重放攻击。缺点是需要额外保存使用过的随机数,若记录的时间段较长,则保存和查询的开销较大。所以一般会采用时间戳+随机数方式的:一分钟以内的+此时间段内不重复的随机数请求才接受(存储采用redis,利用reids的TTL机制自动清理数据)

在实际中,常将方法(1)和方法(2)组合使用,这样就只需保存某个很短时间段内的所有随机数,而且时间戳的同步也不需要太精确。时间戳一般都是客户端生成,而nonce可以由客户端生成、也可以由服务端生成:

  • 服务端生成的话,要额外增加一个接口级客户端单独获取nonce
  • 客户端生成则不需要,可以简化调用逻辑

生成timestamp、nonce,也放到header中做为公共参数,并参与message的拼接:message = 摘要算法(业务参数的json字符串+timestamp+nonce)。这里就不再实现了,代码也不难

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/75082.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STL之map和set

1. 关联式容器 vector、list、deque、 forward_list(C11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。 关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是结…

Vue3 其它API Teleport 传送门

Vue3 其它API Teleport 传送门 在定义一个模态框时,父组件的filter属性会影响子组件的position属性,导致模态框定位错误使用Teleport解决这个问题把模态框代码传送到body标签下

《Python Web网站部署应知应会》No4:基于Flask的调用AI大模型的高性能博客网站的设计思路和实战(上)

基于Flask的调用AI大模型的高性能博客网站的设计思路和实战(上) 摘要 本文详细探讨了一个基于Flask框架的高性能博客系统的设计与实现,该系统集成了本地AI大模型生成内容的功能。我们重点关注如何在高并发、高负载状态下保持系统的高性能和…

力扣刷题-热题100题-第27题(c++、python)

21. 合并两个有序链表 - 力扣(LeetCode)https://leetcode.cn/problems/merge-two-sorted-lists/description/?envTypestudy-plan-v2&envIdtop-100-liked 常规法 创建一个新链表,遍历list1与list2,将新链表指向list1与list2…

AI加Python的文本数据情感分析流程效果展示与代码实现

本文所使用数据来自于梯田景区评价数据。 一、数据预处理 数据清洗 去除重复值、空值及无关字符(如表情符号、特殊符号等)。 提取中文文本,过滤非中文字符。 统一文本格式(如全角转半角、繁体转简体)。 中文分词与去停用词 使用 jieba 分词工具进行分词。 加载自定义词…

Microi吾码界面设计引擎之基础组件用法大全【内置组件篇·上】

🎀🎀🎀 microi-pageengine 界面引擎系列 🎀🎀🎀 一、Microi吾码:一款高效、灵活的低代码开发开源框架【低代码框架】 二、Vue3项目快速集成界面引擎 三、Vue3 界面设计插件 microi-pageengine …

【多线程】单例模式和阻塞队列

目录 一.单例模式 1. 饿汉模式 2. 懒汉模式 二.阻塞队列 1. 阻塞队列的概念 2. BlockingQueue接口 3.生产者-消费者模型 4.模拟生产者-消费者模型 一.单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,其核心思想是确保…

Vuex状态管理

Vuex Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式管理应用的所有组件状态,并以相应的规则保证状态以一种可预测的方式发生变化。(类似于在前端的数据库,这里的数据存储在内存当中) 一、安装并配置 在项目的…

从代码学习深度学习 - 使用块的网络(VGG)PyTorch版

文章目录 前言一、VGG网络简介1.1 VGG的核心特点1.2 VGG的典型结构1.3 优点与局限性1.4 本文的实现目标二、搭建VGG网络2.1 数据准备2.2 定义VGG块2.3 构建VGG网络2.4 辅助工具2.4.1 计时器和累加器2.4.2 准确率计算2.4.3 可视化工具2.5 训练模型2.6 运行实验总结前言 深度学习…

Baklib激活企业知识管理新动能

Baklib核心技术架构解析 Baklib的底层架构以模块化设计为核心,融合知识中台的核心理念,通过分布式存储引擎与智能语义分析系统构建三层技术体系。数据层采用多源异构数据接入协议,支持文档、音视频、代码片段等非结构化数据的实时解析与分类…

C之(16)scan-build与clang-tidy使用

C之(16)scan-build与clang-tidy使用 Author: Once Day Date: 2025年3月29日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 全系列文章可参考专栏: Linux实践记录_Once_da…

在 Vue 项目中快速集成 Vant 组件库

目录 引言一、找到 src 下的App.js 写入代码。二、安装Vant三、解决 polyfill 问题四、查看依赖五、配置webpack六、引入 Vant七、在组件中使用 Vant八、在浏览器中查看样式总结 引言 在开发移动端 Vue 项目时,选择一个高效、轻量且功能丰富的组件库是提升开发效率…

(二)GEE基础学习初探及案例详解【20250330】

Google Earth Engine(GEE)是由谷歌公司开发的众多应用之一。借助谷歌公司超强的服务器运算能力以及与NASA的合作关系,GEE平台将Landsat、MODIS、Sentinel等可以公开获取的遥感图像数据存储在谷歌的磁盘阵列中,使得GEE用户可以方便的提取、调用和分析海量…

妙用《甄嬛传》中的选妃来记忆概率论中的乘法公式

强烈推荐最近在看的不错的B站概率论课程 《概率统计》正课,零废话,超精讲!【孔祥仁】 《概率统计》正课,零废话,超精讲!【孔祥仁】_哔哩哔哩_bilibili 其中概率论中的乘法公式,老师用了《甄嬛传…

蓝桥杯备考---->并查集之 Lake Counting

这道题就统计有多少个连通块就行了 这时候我们又需要把二维转成一维了,也就是把每一个格子都给一个编号 当我们合并连通块的时候,其实是只需要四个方向的 因为我们是从上往下遍历的,我们遍历到某个位置的时候,它已经和上面部分…

Vue3 项目通过 docxtemplater 插件动态渲染 .docx 文档(带图片)预览,并导出

Vue3 项目通过 docxtemplater 插件动态渲染 .docx 文档(带图片)预览,并导出 预览安装插件示例代码项目目录结构截图实际效果截图 动态渲染 .docx 文档(带图片),预览、导出安装插件docx 模板文件内容完整代码…

养老更安心!智绅科技“智慧”养老系统,智在何处?

在老龄化趋势不断加剧的当下,养老问题成为全社会关注的焦点。 人们对于养老服务的需求日益增长,不仅期望能够得到基本的生活照料,更渴望在安全、舒适、便捷的环境中安享晚年。 智绅科技的“智慧”养老系统应运而生,凭借其独特的…

MySQL 当中的锁

MySQL 当中的锁 文章目录 MySQL 当中的锁MySQL 中有哪些主要类型的锁?请简要说明MySQL 的全局锁有什么用?MySQL 的表级锁有哪些?作用是什么?元数据锁(MetaData Lock,MDL)意向锁(Inte…

vue前端代码作业——待办事项

美化样式示意图: 后端IDEA代码示意图: 代码解释: 1. isAllChecked 计算属性的作用 isAllChecked 用于实现 “全选 / 全不选” 功能,它是一个 双向绑定 的计算属性(因为 v-model 需要同时支持读取和设置值&#xff09…

Oracle数据库数据编程SQL<3.1 PL/SQL 匿名块 及 流程控制中的条件判断、循环、异常处理和随机函数应用>

PL/SQL部分 在SQL的基础上增加了一些过程化的控制语句。 过程化控制语句包括:类型定义、判断、循环、游标、异常处理(例外处理) 目录 PL/SQL匿名块 一、匿名块基本结构 1、匿名块由三个部分组成: 2、注意事项: …