首页|Aiwin - 开发
type: channel
link: https://aiwin.net.cn/index.php/tag/%E5%BC%80%E5%8F%91/
items: TianaiCaptcha 开源验证码简单分析
items: 阿里云自动续签SSL证书
items: Go语言基础简单了解
items: NodeJS速学笔记
items: 关于Linux的基础总结
items: 安卓基础学习(续)
items: 安卓基础学习
items: Sringboot2整合shiro实现登录认证、权限控制和记住我功能
items: 数据库大作业-Python Django开发购物网站完整流程
items: 记几道BFS，中心扩展法，动态规划，状态压缩的例题

TianaiCaptcha 开源验证码简单分析
type: item
date: 2025-10-09T17:43:00+08:00
description: 滑块验证码参数 验证码的 ID：SLIDER_ff970847732c44859fb56935fab7f7c9 鼠标 move 的轨迹，一共记录 36 次 滑块从开始到结束的时间 ImageCaptchaTrack(bgImageWidth=300, bgImageHeight=180, templateImageWidth=null, templateImageHeight=null, startTime=Tue Sep 09 17:30:21 CST 2025, stopTime=Tue Sep 09 17:30:24 CST 2025, left=null, top=null, trackList=[ImageCaptchaTrack.Track(x=0.0, y=0.0, t=1244.0, type=down), ImageCaptchaTrack.Track(x=1.0, y=0.0, t=1268.0, type=move), ImageCaptchaTrack.Track(x=3.0, y=0.0, t=1284.0, type=move), ImageCaptchaTrack.Track(x=10.0, y=0.0, t=1301.0, type=move), ImageCaptchaTrack.Track(x=19.0, y=0.0, t=1318.0, type=move), ImageCaptchaTrack.Track(x=34.0, y=0.0, t=1334.0, type=move), ImageCaptchaTrack.Track(x=46.0, y=0.0, t=1351.0, type=move), ImageCaptchaTrack.Track(x=55.0, y=0.0, t=1368.0, type=move), ImageCaptchaTrack.Track(x=63.0, y=-1.0, t=1384.0, type=move), ImageCaptchaTrack.Track(x=75.0, y=-1.0, t=1401.0, type=move), ImageCaptchaTrack.Track(x=91.0, y=-1.0, t=1418.0, type=move), ImageCaptchaTrack.Track(x=113.0, y=-1.0, t=1435.0, type=move), ImageCaptchaTrack.Track(x=122.0, y=-1.0, t=1451.0, type=move), ImageCaptchaTrack.Track(x=132.0, y=-1.0, t=1468.0, type=move), ImageCaptchaTrack.Track(x=143.0, y=-1.0, t=1485.0, type=move), ImageCaptchaTrack.Track(x=156.0, y=-2.0, t=1501.0, type=move), ImageCaptchaTrack.Track(x=166.0, y=-2.0, t=1518.0, type=move), ImageCaptchaTrack.Track(x=172.0, y=-3.0, t=1535.0, type=move), ImageCaptchaTrack.Track(x=173.0, y=-3.0, t=1551.0, type=move), ImageCaptchaTrack.Track(x=174.0, y=-3.0, t=1568.0, type=move), ImageCaptchaTrack.Track(x=176.0, y=-3.0, t=1585.0, type=move), ImageCaptchaTrack.Track(x=182.0, y=-3.0, t=1602.0, type=move), ImageCaptchaTrack.Track(x=193.0, y=-3.0, t=1618.0, type=move), ImageCaptchaTrack.Track(x=206.0, y=-3.0, t=1635.0, type=move), ImageCaptchaTrack.Track(x=217.0, y=-3.0, t=1652.0, type=move), ImageCaptchaTrack.Track(x=218.0, y=-3.0, t=1668.0, type=move), ImageCaptchaTrack.Track(x=219.0, y=-3.0, t=1685.0, type=move), ImageCaptchaTr…

阿里云自动续签SSL证书
type: item
date: 2025-04-26T11:00:00+08:00
description: 前言 由于国内法律法规的限制，免费的SSL证书由1年变成了3个月，对于在弄了一个带有自己域名博客的人来说，这是一件比较无语的事情，每3个月重复性的工作需要手动更换SSL证书。网上也有很多docker版本的自动续签的应用，如宝塔，acme.sh等等。但是安装这种软件的话，功能就多了许多其它的，个人又不需要。因此自己简单弄一个脚本去实现就十分必要。 操作过程 1、首先需要在工作台创建一个子账号，并取得账号分配的key与secret，给子用户分配AliyunDNSFULLAccess与AliyunYunDunCertAccess权限。 2、生成2048位的crs证书，一般以RSA算法为主，通过Linux系统中自带的openssl命令生成即可 #openssl req -new -nodes -sha256 -newkey rsa:2048 -keyout blog.key -out blog.csr -config openssl.cnf [req] default_bits = 2048 prompt = no default_md = sha256 distinguished_name = dn req_extensions = req_ext [dn] CN = aiwin.net.cn emailAddress = test@foxmail.com O = zirong OU = liu L = Guangzhou ST = Guangdong C = CN [req_ext] subjectAltName = @alt_names [alt_names] DNS.1 = aiwin.net.cn DNS.2 = www.aiwin.net.cn 3、通过crontab定时任务每3个月执行脚本，注意脚本中的crs需要带上\r\n或\n，最好通过一行直接写完，否则传输到阿里云接口会因为没有固定格式的分行而报错。 import os import time import logging from alibabacloud_cas20200407.client import Client as cas20200407Client from alibabacloud_credentials.client import Client as CredentialClient from alibabacloud_tea_openapi import models as open_api_models from alibabacloud_cas20200407 import models as cas_20200407_models from alibabacloud_tea_util import models as util_models from alibabacloud_alidns20150109.client import Client as Alidns20150109Client from alibabacloud_alidns20150109 import models as alidns_20150109_models logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') ''' 全局存储OrderId与RecordId ''' OrderIdList = [] RecordIdList = [] class SSL: def __init__(self, client, endpoint): self.client = self.create_client(client, endpoint) ''' 环境变量设置id与secret,根据不同的方法与endpoint创建不同的安全功能客户端 @ALIBABA_CLOUD_ACCESS_KEY_ID @ALIBABA_CLOUD_ACCESS_KEY_SECRET ''' @staticmethod def create_client(func, endpoint): credential = CredentialClient() config = open_api_models.Config( credential=credential, region_id='cn-hangzhou', endpoint=endpoint, connect_timeout=5000, read_timeout=5000 ) return func(config) ''' 请求创建SSL证书并获取订单ID ''' @staticmethod def main(…

Go语言基础简单了解
type: item
date: 2024-01-03T20:47:00+08:00
description: 前言 简单的入门一下Go、会对基础语法、网络编程、Gin开发进行简单的了解，关键是Gin开发。 关于Go Go语言（也称为Golang）是Google开发的一种开源编程语言。它被设计用于构建高效、可靠和可扩展的软件系统。下面是Go语言的一些主要用途： 服务器端开发：Go语言提供了强大的标准库和并发模型，使其成为构建高性能网络服务器的理想选择。许多大型互联网公司正在使用Go语言来开发后端服务，以处理高负载和并发请求。 网络编程：Go语言提供了丰富的网络编程库，可用于开发各种网络应用程序，包括Web服务、API服务器、网络代理等。 分布式系统：Go语言的并发模型和原生支持的并发原语（goroutine和channel）使其非常适合构建分布式系统，例如数据处理管道、消息队列等。 命令行工具：Go语言的编译速度快，生成的可执行文件体积小，使其成为开发命令行工具的良好选择。许多开发者使用Go语言来构建工具、脚本和自动化任务。 嵌入式系统：Go语言可以用于编写嵌入式系统的控制逻辑和驱动程序。它提供了对底层硬件的访问和控制能力，并具有较小的内存消耗。 需要注意的是，Go语言具有简洁而直观的语法，易于学习和使用。它的性能非常好，可以充分利用多核处理器和并发编程来提高应用程序的性能和吞吐量。 学习流程 基础语法->Web开发->常见中间件->云平台 基础语法 注释 增强语言的可读性 单行注释 多行注释 package main import "fmt" // 单行注释 /* 多行注释 多行注释 */ func main() { fmt.Println("hello world") } 变量 var 定义变量，var 变量名 变量类型。 简短变量声明，使用:=运算符可以在函数内部声明并初始化变量。 匿名变量，使用 _ 占位符可以声明一个匿名变量，忽略不需要的值，任何赋值給这个标识符的值都将被抛弃，并且不会导致变量的冲突 定义多个变量，可以使用()包裹，表示定义多个变量 注意点：变量名的首个字符不能为数字，全局变量可被局部变量再定义(就近原则 )，定义的变量一定要使用。 变量声明后的默认值： 整数型浮点数变量默认值是0和0.0 字符串变量默认值是空字符串 布尔型变量默认是false 切片，函数，指针变量默认是nil Printf输出时声明的格式 %v：默认格式化输出，会根据变量的类型自动选择合适的格式。 %s：输出字符串。 %d、%b、%o、%x：输出整数，分别表示十进制、二进制、八进制和十六进制。 %t：输出布尔值，结果为 true 或 false。 %f、%e、%g：输出浮点数，分别表示十进制表示法、科学计数法和通用格式。 %p：输出指针地址。 %c：输出字符。 %q：输出带引号的字符串。 %%：输出一个百分号。 fmt.Printf("内存地址:%p,变量类型:%T",name,&name) //打印内存地址，变量类型等 例子： package main import "fmt" var name = "Lau" //全局变量(隐式定义) func fun() (int, int) { return 100, 200 } func main() { var name string = "aiwin" //显示定义 var a, b int //同时定义a,b两个变量 age := 18 fmt.Printf("姓名:%s,内存地址为:%p,类型为:%T,年龄:%d,年龄十六进制数为:%x\n", name, &name, name, age, age) //就近原则 a, _ = fun() _, b = fun() fmt.Println("_可代替被舍弃的值,并且可被重复定义:", a, b) a, b = b, a fmt.Println("类似于Python,可直接进行值交换:", a, b) } 常量 使用const来定义常量，不可改变 iota 开始是0，默认会不断的自增进行计数，相当于是一个常量的计数器，直至新的一组常量计数器出现才会恢复，可理解过const语句块的索引 package main import "fmt" func main() { const ( a = iota b c d = "aiwin" e //未被定义所以用上一个的值,但是iota还是会一直计数 f = 100 g //未被定义所以用上一个的值 h = iota j ) const ( k = iota //新的const出现,新iota被重新从0开始计数 l ) fmt.Println(a, b, c, d, e, f, g, h, j, k, l) //0 1 2 aiwin aiwin 100 100 7 8 0 1 } 数据类型 布尔型boo…

NodeJS速学笔记
type: item
date: 2024-01-03T20:43:00+08:00
description: 一、什么是Node.js? 基于Chrome V8引擎的Javascript的运行环境，V8相对于火狐的OdinMonkey，Safri浏览器的JSCore都要快。 Node.js是Javascript的后端运行环境，无法调用DOM和BOM等浏览器内置API，仅仅提供了基础的功能和API，这些基础使很多强大的框架出现，可以基于Express框架构建Web应用，基于restify快速构建API项目，可以读写和操作数据库等。 二、fs文件系统模块 fs模块是Node.js官方提供的用来操作文件的模块，提供一系列的方法和属性，用来满足用户对文件的操作，有如下的一些常用命令。 命令 说明 open(path，mode，callback) 异步模式下打开文件 stat(path，callback) 异步模式下获取文件信息 writeFile(file，data，callback) 异步模式下写入文件 read(fd，buffer，offset，length，position，callback) 通过文件描述符读取文件 close(fd，callback) 通过文件描述符读取文件 ftruncate(fd，len，callback) 通过文件描述符读取文件 fs.unlink(path, callback) 删除文件 fs.mkdir(path[, options], callback) 创建目录 fs.readdir(path, callback) 读取目录 fs.rmdir(path, callback) 删除目录 fs.exists(path, callback) 文件是否存在 例子： const fs=require('fs') const buf = new Buffer.alloc(1024); fs.mkdir('../file/',function (err) { if(err){ console.log(err) } console.log("目录创建成功") }) fs.writeFile('../file/input.txt','通过writeFile写入文件内容',function (err) { if(err){ return console.log(err) } console.log('文件写入成功') }) fs.readdir("../file/",function (err,files) { if(err){ return console.error(err) } files.forEach(function (file) { console.log("目录读取开始") console.log(file) }) }) fs.open('../file/input.txt','r+',function (err,fd) { if(err){ return console.error(err) } console.log("开始文件读取") fs.read(fd,buf,0,buf.length,0,function (err,bytes) { if(err){ console.log(err) } if(bytes>0){ console.log(buf.subarray(0,bytes).toString()) } console.log(bytes+"字节被读取") }) fs.close(fd,function (err) { if(err){ console.log(err) } console.log("文件读取关闭") }) }) fs.unlink("../file/input.txt",function (err) { if(err){ return console.error(err) } console.log("文件删除成功") }) setTimeout(function() { fs.rmdir("../file/", function (err) { if (err) { return console.log(err) } console.log("目录删除成功") }) },3000) path路径模块 Node在读取或者写入文件，使用相对路径的时候，会以Node使用的目录为起点，直接拼接相对目录的字符串，可以使用绝对路径或者path模块解决。 命令 说明 sep 平台路径分割符 join() 拼接路径 basename() 最后的文件名 extname() 文件扩展名 isAbsolute() 判断是否为绝对路径 relative 转为相对路径 normalize 规范化路径 const path=require('path') console.log(path.sep) …

关于Linux的基础总结
type: item
date: 2024-01-03T20:41:00+08:00
description: 前言 这学期学习了Linux课程，对Linux的常用命令写个总结。 一、为什么Linux如此流行？ 1.原因 作为目前世界上最受欢迎的系统，近乎世界超级计算机Top10中全是Linux系统的身影。 Linux流行的背后，原因是什么呢?： 1、Linux稳定且有效率。 2、Linux开源且免费，有强大的社区支持。 3、Linux拥有更加安全的用户与文件权限策略。 4、Linux易于维护，拥有超强的定制性 当然，个人认为，Linux流行的关键原因在于开源且免费，强大的社区支持。 2.Linux系统的版本 Linux的发行版本有很多，主要流行的如下： 1.Red Hat Linux 2.CentOs，与RedHat企业版完全兼容 3、Ubuntu、Debian、Kali 4、国内的OpenEuler、OpenAnolis 二、Linux的基础命令 1.目录结构 Linux的目录结构如上，/etc存放着系统配置文件的目录，/home是除root外的其它用户的目录，/dev存放设备文件，dev存放设备文件，boot存启动文件，tmp是临时目录，usr存放库文件，文档，命令，用户数据等，var存放变化的文件，如日志文件等， 目录名 解析 bin 存放普通用户可执行命令 etc 系统的配置文件 root 超级用户目录 dev 系统的一些设备文件，驱动等 boot 系统的启动文件 tmp 临时文件存放目录 usr 存放用户的应用程序和文件 var 存放一些变化的文件，如程序日志等 home 普通用户的目录 run 存放进程产生的临时文件 mnt 临时的挂载文件系统 sbin 存放系统管理员才能执行的命令 2.文件命令 1.ls 列出该目录下的子目录与文件，常用参数： -a 以.开头的隐藏项目也列出 -l 列出目录的详细信息，可直接使用ll 2.pwd 显示当前所处目录 3.cd 切换工作的目录，可使用相对路径与绝对路径，相对路径切换多个目录可以使用../..这种格式 4.touch、mkdir touch是不存在文件则创建文件，存在则修改文件时间。 mkdir是创建目录 5.cat、tail、head、tail、od、tee、more、less cat 直接输出文件的全部内容 head 输出文件开头的内容、-n 指定行数 tail 输出文件末尾的内容，-n输出尾n行 more 按页显示文本文件内容，只允许向前浏览 less 与more类似，允许向前向后浏览 tee 将文件的输出复制到另一个文件 od 将文件内容以指定的禁止显示，-t指定进制 6.rm、cp、mv rm删除某个文件，常用rm -r 用于删除目录 cp复制文件，-r可以复制目录 mv移动文件 7.find、grep、xargs find 搜索文件命令，可通过通配符搜索某个文件 grep，以正则表达式进程全局查找匹配行 命令传递参数的过滤器，可将管道或标准输入数据转换成命令行参数，也可从文件中读取数据 三者合一、可用于搜索文件的内容 8.tar、zip、uzip、gzip 1.tar将文件打包或压缩或解包，压缩文件后缀有gz、bz、xz，常用参数 -c产生.tar打包文件，-x解包、-zjJ分别以gz、bz2、xz打包或解包文件。 2.zip将文件打包，常用-r递归打包目录 3.uzip将文件解包 4.gzip压缩命令，只能压缩文件，不能压缩目录 9.ln 创建软或硬连接，可以理解为相当于创建快捷方式，硬连接的链接文件和被链接文件必须在同一目录且不能建立指向目录的硬连接，链接的源文件消失，链接即失效。常用参数：-s 创建符号链接即软连接，-P，创建直接指向符号链接的硬链接，-f删除已存在的目标文件。 10.lsattr和chattr chattr 与 lsattr 分别用于改变和显示文件属性，与 chmod 命令相比，chmod 只改变文件的读写、执行权限，更底层的属性控制是由 chattr 来改变。 chattr主要常用的参数有：-R 递归改变指定目录下的文件属性，+在原有的参数设定基础上、追加参数，-在原有的参数设定基础上、移除参数。 lsattr常用参数：-R 递归显示目录和文件属性、-a 显示所有隐藏文件属性、 chattr可以改变的文件属性有： 属性 含义 a 只允许向文件追加数据，不允许删除和修改文件内容。如果目录有具有这个属性，系统将只允许在目录下简历和修改文件，不允许删除任何文件。只有root才能设置该属性 c 自动将文件压缩，在读取时自动解压缩 d 在进行文件系统备份时，dump指令忽略此文件 e 在ext文件系统中，表示该文件使用区段(extents)映射磁盘上的块 i 不允许对文件进行任何的修改。对于目录而言，只能修改目录之下的文件，不允许建立和删除文件。只有…

安卓基础学习(续)
type: item
date: 2024-01-03T20:40:00+08:00
description: 十一、综合训练（购物车功能） 实现功能： 手机商品页面展示，加入购物车功能，商品详情页面，清空购物车，删除购物车商品。 数据库准备，准备的数据库有商品数据库，还有存储购物车信息的数据库，使用SqliteOpenHelper进行操作，商品数据库的字段有id，description，price，picPath，购物车字段id，goodsId，count，实现如下： package com.example.shoppingcart.entity; //购物车信息 public class CartInfo { public int id; // 商品编号 public int goodsId; // 商品数量 public int count; public CartInfo(){} public CartInfo(int id, int goodsId, int count) { this.id = id; this.goodsId = goodsId; this.count = count; } } package com.example.shoppingcart.entity; import com.example.shoppingcart.R; import java.util.ArrayList; public class Goodsinfo { public int id; public String name; public String description; public float price; public String picPath; public int pic; private static String[] mNameArray = { "iPhone11", "Mate30", "小米10", "OPPO Reno3", "vivo X30", "荣耀30S" }; // 声明一个手机商品的描述数组 private static String[] mDescArray = { "Apple iPhone11 256GB 绿色 4G全网通手机", "华为 HUAWEI Mate30 8GB+256GB 丹霞橙 5G全网通 全面屏手机", "小米 MI10 8GB+128GB 钛银黑 5G手机 游戏拍照手机", "OPPO Reno3 8GB+128GB 蓝色星夜 双模5G 拍照游戏智能手机", "vivo X30 8GB+128GB 绯云 5G全网通 美颜拍照手机", "荣耀30S 8GB+128GB 蝶羽红 5G芯片 自拍全面屏手机" }; // 声明一个手机商品的价格数组 private static float[] mPriceArray = {6299, 4999, 3999, 2999, 2998, 2399}; // 声明一个手机商品的大图数组 private static int[] mPicArray = { R.drawable.iphone, R.drawable.huawei, R.drawable.xiaomi, R.drawable.oppo, R.drawable.vivo, R.drawable.rongyao }; public static ArrayList<Goodsinfo> getDeafultList(){ ArrayList<Goodsinfo> goodsList=new ArrayList<Goodsinfo>(); for(int i=0;i<mNameArray.length;i++){ Goodsinfo goodsinfo=new Goodsinfo(); goodsinfo.id=i; goodsinfo.name=mNameArray[i]; goodsinfo.description=mDescArray[i]; goodsinfo.price=mPriceArray[i]; goodsinfo.pic=mPicArray[i]; goodsList.add(goodsinfo); } return goodsList; } } package com.example.shoppingcart.database; import android.annotation.SuppressLint; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sq…

安卓基础学习
type: item
date: 2024-01-03T20:39:00+08:00
description: 前言 最近在有在持续学习Java的安卓开发，不断的把知识记录下。 工具 Android Studio安装 Studio安装 SDK下载 SDK即软件开发工具包，可将App源码编译成可执行的App应用 创建项目 创建项目 这里Minimum SDK后面指的是兼容到安卓几，表示你的应用支持的最低版本，看百分比选，一般目前的都是9.0-10.0。 内置模拟器 初始化Gradle要下载半小时，内置模拟器也有十多G，默认在C盘很不爽，可以移动， 注册环境变量，然后修改下面的配置，直接把目录移动到想存的位置即可。 观察App运行日志 Android采用Log日志打印日志，各类日志划分： Log.e 表示错误信息，可能导致程序崩溃 Log.w：表示警告信息 Log.i：表示一般消息 Log.d：表示调试信息，可把程序运行变量值打印，方便跟踪调试 Log.v：表示冗余信息 真机调试 1、使用数据库连接到电脑 2、电脑上会自动安装USB存储设备驱动 3、打开手机的开发者选项并开启USB调试 4、将连接的手机设备文件传输模式，并允许计算机进行USB调试 关于安卓开发语言 App开发分为两大技术路线，分别为原生开发和混合开发，官方的编程语言包括Java和Kotlin。 混合开发渲染性能没有原生开发好，但是混合开发可以跨平台，版本更新不需要重新下载API文件。 关于C和C++，Java是解释型语言，在处理图像和音视频时性能显然就有瓶颈，而C和C++是编译型语言，会 先翻译成机器语言，在图像和音视频处理时可以调用java的JNI接口调用C和C++程序进行处理，也称NDK。 目录结构 mainfests：App运行配置文件 java子目录：存放java源代码，后面两个包存放测试用的代码 res目录下： drawable：存放图形描述和图片文件 Layout：App页面的布局文件 mipmap：App的启动图标 values：存放常量的文件，如样式风格等 Gradle Scripts目录下： build.gradle：描述项目级和模块级的App工程编译规则 proguard-rules.pro：java代码的混淆规则，保证安全性 gradle.properties：用于编译工程的命令行参数 local.properties：本地配置文件，描述开发者电脑的环境配置 settings.gradle：配置了需要编译的模块 build.gradle的文件概述解释 plugins { //使用的插件 id 'com.android.application' } android { namespace 'com.example.myapplication' compileSdk 33 //编译使用的SDK版本号,33表示使用Android13 defaultConfig { // App的包名 applicationId "com.example.myapplication" //指定App适合运行的最小的SDK版本号,28表示最小在Android9上运行 minSdk 28 //目标设备的版本号,表示App最希望在那个版本的Android上运行 targetSdk 33 //App的应用版本号 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } //跟单元测试相关 buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } //指定App编译的依赖包信息 dependencies { //指定编译的Android高版本支持库 implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'com.google.android.material:material:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' //指定单元测试编译用的junit版本号 testImplementation 'junit:junit:4.13.…

Sringboot2整合shiro实现登录认证、权限控制和记住我功能
type: item
date: 2024-01-03T20:35:00+08:00
description: 前言 很久没写过文章了，最近课程设计硬性要求要使用Spring Secure或shiro其中一种安全框架，因为shiro的轻便更被广泛使用，所以学了一下shiro框架的基本使用，也来了点兴致，所以记录一下。 提示：以下是本篇文章正文内容，下面案例可供参考 一、关于Shiro Apache Shiro 是一个功能强大且易于使用的Java安全框架，为开发人员提供了一种直观而全面的身份验证，授权，加密和会话管理解决方案，提供了身份验证、授权、密码学、会话管理四大基本安全功能、还提供一些缓存、记住我、并发等多种额外功能解决一些问题。 Shiro的一些核心概念有：Realms、SecutiryManager、Subjuect。 关于Subject可直接理解为对象，用于和应用进行交互，通过Subject传入到安全管理器。 关于SecurityManger就是安全管理器，所有的安全性操作都与它进行交互，是Shiro管理的核心。 关于Realms可以理解为存储域，存储Shiro的一些安全数据等，安全管理器需要从Realms获取数据，验证是否合法。 大概就这些，学的比较粗浅，底层源码也没咋了解，直接就学了如何使用。 二、功能实现 1.创建User表的数据库 实现Shiro与数据库交互，能够通过手机号+密码或邮箱+密码进行登录验证。 这里的数据库字段设置如上、Id号（主键）、name（姓名）、phone（手机号）、username（用户名）、password（密码）、email（邮箱）、state（状态）。 2.引入依赖 pom.xml文件如下： <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.4</version> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>shiroDemo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>shiroDemo</name> <description>shiroDemo</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.17</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.sprin…

数据库大作业-Python Django开发购物网站完整流程
type: item
date: 2024-01-03T20:21:00+08:00
description: 前言 一、需求分析 1，功能设计 二、数据库设计 1，概念设计 2，逻辑设计 3，物理设计： 四，系统功能设计 1，功能层次图： 2，系统功能模块详细设计 五，技术环境 六、代码实现 目录结构图： 实现浏览: 前言 数据库项目开发，要经历需求饭呢西，概念数据，逻辑设计，物理设计，功能设计等几个阶段，学习数据库系统理论时，结合了项目的开发，可能做的很菜，但是也写篇博客总结一下。 提示：以下是本篇文章正文内容，下面案例可供参考 一、需求分析 1，功能设计 B2B的商业平台功能：注册，商品上架，需求上架，下单，购物车，用户个人中心这些增删改查的功能。 数据流图： 功能流程图： 商品上架流程图 下单商品流程图 购物车业务图 需求发布流程图 个人中心流程图 二、数据库设计 1，概念设计 E-R图： 2，逻辑设计 客服（ _ _客服ID ， _ _用户ID，商品ID ，用户姓名，商品名称,...） 用户（ _ _用户ID ，用户姓名， _ _需求ID，订单ID ,...） 需求（ _ _需求ID ，发布人， _ _订单ID ，商品名，...） 商品（ _ _商品ID ，商品名， _ _类别ID ，类别名，...） 类别（ _ _类别ID ，类别名， _ _订单ID，商品ID ，...） 订单（ _ _订单ID， _ _订单细节ID ，收货人， _ _物流ID__ ，...） 订单细节（ _ _订单细节ID ， _ _订单ID，商品ID ，商品名，...） 物流状态（ _ _物流ID ， _ _订单ID ，...） 3，物理设计： user表:存储用户的各种信息 Product:存储商品的各种信息 Request:存储需求的各种信息 Product_category:存储商品的各种类别信息 Orders:存储订单信息 Orders_detail:存储订单细节信息 Logistic:存储订单的物流状态 User表: 存储方式:通过用户的ID进行索引递增形式的存储 属性名 | 存储名 | 变量类型 | 约束条件 | 说明 ---|---|---|---|--- 用户号 | ID | Int(10) | 主键 | 姓名 | Name | Char(10) | 非空 | 账号 | Username | Char(20) | 非空 | 密码 | Password | Char(20) | 非空 | 密码干扰 | Salt | Char(5) | 非空 | 联系电话 | Phone | Char(11) | 非空 | 行业 | Business | Char(20) | 无 | 权限 | Jurisdiction | Int(1) | 非空 | 0普通用户,1客服 创建时间 | Create_time | Datetime | 非空 | | 格式:2022-02-22 12:01:01 | ---|--- 修改时间 | Upadate_time | Datetime | 非空 | 格式:2022-02-22 12:01:01 状态 | status | Int(1) | 非空 | Product表: 存储方式：聚簇存储，通过商品的类别ID进行一类一类的存储 属性名 | 存储名 | 变量类型 | 约束条件 | 说明 ---|---|---|---|--- 商品号 | id | Int(10) | 主键 | 姓名 | user_name | Char(10) | 非空 | 联系电话 | phone | Char(11) | 非空 | 商品名称 | Name | Char(20) | 非空 | 类别 | category_id | Char(10) | 非空 | 图片 | picture | Varchar(50) | 非空 | 商品描述 | description | Varchar(50) | 非空 | 发货地址 | address | Varchar(50) | 非空 | 单价 | price | Double(10,2) | 非空 | 状态 | state | Int(1) | 非空 | 0审核中,1已上架,2已下架 创建时间 | create_time | Datetime | 非空 | 格式:2022-02-22 12:01:01 修改时间 | update_time | Datetime | 非空 | 格式:2022-02-22 12:01:01 Request表: 存储方式：根据商品的类别进行聚簇存储 属性名 | 存储名 | 变量类型 | 约束条件 | 说明 ---|---|---|---|--- 需求号 | Id | Int(10) | 主键 | 发布人 | name | Char(10) | 非空 | 发布日期 | create_time…

记几道BFS，中心扩展法，动态规划，状态压缩的例题
type: item
date: 2024-01-03T20:19:00+08:00
description: 目录 前言 一、中心扩展法 二、BFS 三、状态压缩 四、动态规划 总结 前言 在学习数据结构的时候学到了不少算法思想，但是那时候真的很难，直到现在发现有些算法思想不懂不行，所以去刷了不少题，在此记录下印象比较深的题。 提示：以下是本篇文章正文内容，下面案例可供参考 一、中心扩展法 1、个人理解：中心扩展法的思想就是在解决某个事情的时，以某点为中心，向着左右两边扩散，直至某个条件不满足或满足时再返回结果，或者以下一个点为中心继续进行相同的扩展运算。 2、例题：最长回文字符串 题目简介：在一个英文字符串L中，找出最长的回文字符串，如L="caayyhheehhbbbhhjhhyyaac"，那么它最长的回文字串是"hhbbbhh"，L的长度可能接近10的4次方，要考虑时间和空间复杂度的问题。 自己想的时候：能不能使用暴力的做法，直接遍历所有的子字符串，从所有的子字符串中判断回文的字符串，然后从回文字符串中返回长度最大的。再仔细想想，L的长度可能接近10的4次方，那么子字符串就有2^(10^4)-1种，还要判断这么多种字符串是否回文，时间消耗必然很大。 最优解法：中心扩展法 分析题目：题目包含的情况其实有以下几种 第一种：当字符串只有1个的极端情况，则直接返回本身即可 第二种：以某个字符串为中心，在不超出索引的情况下向两边扩展，假若中心点的字符与左边一个或右边一个字符相等，则中心字符串往后移一位，回文字符串长度+1。比如"abcmm"，以m为中心点时，右边字符与它相等，则mm就是一个回文字符串，中心点往后移，长度+1。 第三种：第二种情况不符合的情况下，比如"abcdca"，当从a开始为中心扩散，中心到c时，判断左边一个字符是否和右边一个字符相等，相等则回文长度+2，继续往左边和右边的第二个遍历，直到不满足情况就返回。 最终：在不超过索引的情况下，遍历字符串，以每个字符会中心，进行扩散，记录最大的回文字符串长度即可，这样即使L的长度可能接近10的4次方，也只需要遍历10^4次。 代码： def max_Palindrome_string(s): length = len(s) # 判断字符串的极端情况 if length == 1: return s elif length == 2 and s[0] != s[1]: return s[0] elif length == 2 and s[0] == s[1]: return s max_len = 1 max_str = s[0] for i in range(length): # 以每个字符为中心进行左右扩散 cur_len = 1 # 记录当前回文字符串的长度 right = i + 1 # 右边字符 left = i - 1 # 左边字符 while left >= 0 and s[i] == s[left]: # 第二种情况,当前字符和左边一个相等或者右边一个相等,则往当前字符往后移一个,回文字符长度+1 cur_len += 1 left -= 1 while right < length and s[i] == s[right]: cur_len += 1 right += 1 while right < length and left >= 0 and s[right] == s[left]: # 第三种情况,当第二种情况不相等,左边字符等于右边字符,回文字符长度+2 cur_len = cur_len + 2 right = right + 1 left = left - 1 left += 1 # 不满足循环后left+1,记录回文字符字串最左边的标记 if cur_len > max_len: # 当前回文字符串长度与记录到的最大回文长度比较 max_len = cur_len max_str = s[left:left + max_len] # 返回最大的回文字符串 return max_str if __name__ == '__main__': string = 'caayyhheehhbbbhhjhhyyaac' print(max_Palindrome_string(string)) 二、BFS 1、个人理解：以图的路径搜索来讲，BFS就以节点的方法搜索遍历图的所有路径，返回需要的结果。 2、例题： 一马当先：马只能走'日'字形（包括旋转90°的日），现在想象一下，给你一个n行m列网格棋盘， 棋盘的左下角有一匹马，请你计算至少需要几步可以将它移动到棋盘的右上角。 题目分析：马走日，一种有多少种情况呢，左上，左下，右上，右下，日也可以是躺着的日，一共有8种情况，以二维坐标轴表示，假如马在中心(0,0)点，那么马就只能跳到(2，-1)，（1，-2），（-1…