做网站公司怎么找客户,网站模板 可做采集站,哪儿提供邯郸做网站,做网站建设怎么找客户【JavaEE】进阶 个人博客系统#xff08;3#xff09; 文章目录 【JavaEE】进阶 个人博客系统#xff08;3#xff09;1. 加盐加密验密算法原理1.1 md5加密1.2 md5验密1.3 md5缺漏1.4 加盐加密1.5 后端的盐值拼接约定1.6 代码实现1.6.1 加密1.6.2 验密1.6.3 测试 2. 博客… 【JavaEE】进阶 · 个人博客系统3 文章目录 【JavaEE】进阶 · 个人博客系统31. 加盐加密验密算法原理1.1 md5加密1.2 md5验密1.3 md5缺漏1.4 加盐加密1.5 后端的盐值拼接约定1.6 代码实现1.6.1 加密1.6.2 验密1.6.3 测试 2. 博客注册页2.1 上传头像2.1.1 期待效果2.1.2 约定前后端交互接口2.1.3 后端代码2.1.4 前端代码2.1.5 测试 2.2 注册2.2.1 期待效果2.2.2 约定前后端交互接口2.2.3 后端代码2.2.4 前端代码2.2.5 测试 3. 博客登录页3.1 期待效果3.2 失焦更新头像3.2.1 约定前后端交互接口3.2.2 后端代码3.2.3 前端代码3.2.4 测试 3.3 处理url 以及 注册页面跳转3.3.1 通过key获取url中的value3.3.2 将username赋值给用户名输入框3.3.3 注册页面跳转3.3.4 测试 3.4 登录功能3.4.1 约定前后端交互接口3.4.2 后端代码3.4.3 前端代码3.4.4 测试 【JavaEE】进阶 · 个人博客系统3
本文章正式进行前后端交互了
还是一样的老套路
根据期待效果约定前后端交互接口后端代码 三板斧校验处理请求返回响应 前端代码 三板斧校验发送请求处理响应
先写后端还是先写前端个人习惯问题~
大方向就是那三板斧具体按具体改动~
1. 加盐加密验密算法原理
1.1 md5加密
我们原本通过md5进行加密这是一个不可逆的加密 在这里插入图片描述](https://img-blog.csdnimg.cn/dbcab6e1574148c0ab41849ba13fc77b.gif)
原理就是通过password生成一个 一一对应 的固定长度的加密密码
1.2 md5验密
为什么不说是解密的因为这个是一个不可逆的过程也就是说如果后端用md5加密后是无法获取到原密码的除非你使用“逆天的暴力枚举”
而一个固定的password生成的是一个固定的加密密码
这个也是常识因为我们几乎在任何场景下都没有遇到过找回密码是返回原密码的一般都是通过一些手段验证你的信息进行修改密码的操作~
所以后端能做的就是“验证密码”
因为一个固定的password生成的是一个固定的加密密码
所以如果密码是正确的话生成的加密密码也是正确对应的上的 1.3 md5缺漏
没错不良用户/黑客可以通过“逆天的暴力枚举”也就是他们总结出来的“彩虹表”
这个表记录了很多很多字符串的加密密码这样如果攻破了数据库的话这些用户的密码就会被破解出来
1.4 加盐加密
加盐这里是比较形象的说法也就是加点料让加密密码无规律
生成一个全球不重复的随机的盐值盐值 原密码进行md5加密获取加密密码将盐值 加密密码保存到数据库
而这个盐值可见就是UUID
因此同样的原密码由于UUID不会重复和md5加密的一一对应生成的加密密码是不一样的~UUID和md5都是用不完的底层不需要理解坐享其成即可不要杞人忧天~ 这个算法逻辑上是破解的了的 攻破数据库后获取一个杂合密码破解出盐值和加密密码不良用户不知道这个盐值 加密码是咋组合的用彩虹表破解加密密码获取原生组合破解出原密码 不良用户不知道这个盐值 原密码是咋组合的很难映射出这么“主观性这么强随机性这么强”的原生组合 从逻辑分析上可以看出破解难度和成本高出的倍数是不能计量的“逆宇宙级枚举” 但是世界上没有完全的安全只有你想不到的破解方法和他们考虑成本是否要进行破解 补充加密过程后端是不会记录下来的这里黑客破解的是持久化的数据 1.5 后端的盐值拼接约定
盐值跟原密码直接拼接生成的加密密码盐值跟加密密码以[salt]$[plus password] 格式拼接 这样方便获取盐值
1.6 代码实现
1.6.1 加密
创建一个用户相关的工具类UserUtils public class UserUtils {public static String encrypt(String password) {// 1. 获取盐值String salt UUID.randomUUID().toString();// 2. md5加密String plusPassword DigestUtils.md5DigestAsHex((salt password).getBytes(StandardCharsets.UTF_8));// 3. 将盐值和加密密码组合返回return salt $ plusPassword;}
}1.6.2 验密
根据加密原理推算
验密就是根据数据库里的组合密码
获取到[盐值]和[正确的加密密码][盐值]拼接[待验证的密码]生成[待验证的加密密码]对比[正确的加密密码]和[待验证的加密密码]
根据md5的一一对应如果对应的上那就是正确的密码
public static boolean confirm(String password, String dbPassword) {// 1. 获取到[盐值]和[正确的加密密码]String[] group dbPassword.split(\\$); // 在split函数的参数字符串里这个$有特殊含义需要转义一下// 2. md5加密String plusPassword DigestUtils.md5DigestAsHex((group[0] password).getBytes(StandardCharsets.UTF_8));// 3. 对比return group[1].equals(plusPassword);
}参数校验调用这些方法之前就确认过了不必重复~
dbPassword必然是正确样式的数据否则之前就不会添加成功获取不到也更不会调用这个方法~
1.6.3 测试
public static void main(String[] args) {String password abcd;String dbPassword1 encrypt(password);boolean conf1 confirm(password, dbPassword1);String dbPassword2 encrypt(password);boolean conf2 confirm(password, dbPassword2);System.out.println(password);System.out.println(dbPassword1);System.out.println(conf1);System.out.println(dbPassword2);System.out.println(conf2);
}结果 补充UUID的-建议去除我的数据库是65位的组合密码所以得去掉
这样UUID就是十六进制的三十二位数了
public static String encrypt(String password) {// 1. 获取盐值String salt UUID.randomUUID().toString().replace(-, );// 2. md5加密String plusPassword DigestUtils.md5DigestAsHex((salt password).getBytes(StandardCharsets.UTF_8));// 3. 将盐值和加密密码组合返回return salt $ plusPassword;
}2. 博客注册页
2.1 上传头像
2.1.1 期待效果 之前用的是form表单上传文件现在我们用Ajax上传文件这样我们就不会被强制跳转且可以获取传递回来的文件名更新其显示 2.1.2 约定前后端交互接口
后端
/user/picture接受请求中的文件项目外的D:/blog_userImage目录下 不用导致项目空间占用太大应该是项目映射指向机器的某一个位置的资源 返回文件名包装成的CommonResult对象
前端
/user/picturemultipart/form-datapostbody文件按钮上传的文件
2.1.3 后端代码
工具类ImageUtils通过getImageUniquePath方法可获取文件保存路径
public class ImageUtils {public static String getImageUniquePath(String originName) {String path blog_userImage/;// 获取唯一idString id UUID.randomUUID().toString();//获取文件后缀String suffix originName.substring(originName.lastIndexOf(.));//拼接path id suffix;return path;}
}controller层处理请求调用service层进行数据持久化
RestController
RequestMapping(/user)
public class UserController {Autowiredprivate UserService userService;RequestMapping(/picture)public CommonResult picture(RequestPart(myfile)MultipartFile file) throws IOException {if(file null) {return CommonResult.fail(-1, 上传文件失败);}//获取文件保存路径String path ImageUtils.getImageUniquePath(file.getOriginalFilename());//通过文件保存路径将文件进行保存userService.loadImage(file, path);//返回文件名包装成统一对象return CommonResult.success(path);}}service层进行数据持久化
Service
Slf4j
public class UserService {public void loadImage(MultipartFile file, String path) {log.info(保存成功 path);//保存成功日志//保存文件try {file.transferTo(new File(D:/ path));//spring mvc是可以直接throws异常的框架内部/异常处理器有处理但是多级调用耦合度有点高} catch (IOException e) {e.printStackTrace();}}
}
配置静态资源映射 对于新增的文件 我们的网站能够访问到我们自己的静态资源是因为我们在运行的时候将这些打包到target里面了而新增的文件只是在我们开发的时候的路径下并没有立即加载到target里绝对路径也是一样无论你保存到项目里还是保存到项目外都没有加载到target里面我们也无法手动写入target 而我们的网站浏览器考虑到安全性是不能直接访问不在项目target里的静态资源的 而我们spring boot项目与普通maven项目不同spring boot项目修改静态资源例如html/css/js等等必须保存并重启服务器才能更新~ 所以我们需要进行静态资源的映射
Configuration
public class MyWebMvcConfigurerAdapter implements WebMvcConfigurer {/*** 配置静态访问资源* param registry*/Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler(/blog_userImage/**).addResourceLocations(file:D:/blog_userImage/);}}含义就是将“file:D:/blog_userImage/”路径下的静态资源映射成“/blog_userImage/**”
通过localhost:8080/blog_userImage/**就可以访问~
并且访问服务器的路由以这个为准拦截器配置 目录结构 2.1.4 前端代码
div idfileImageinputiditypebuttonvalue请上传头像onclickputImage();/input idf_file typefile namefile styledisplay: none /
/divputImage(点击普通按钮触发file按钮)
function putImage() {javascript: jQuery(input[namefile]).click();
}file按钮上传成功触发的事件发送请求
固定搭配无需多问~
jQuery(#f_file).change(function (e) {// 获取选中的文件var file e.target.files[0];// 创建一个 FormData 对象var formData new FormData();formData.append(myfile, file);// 用 Ajax 向服务器发送文件jQuery.ajax({url: /user/picture,type: POST,data: formData,processData: false, // 告诉 jQuery 不要处理发送的数据contentType: false, // 告诉 jQuery 不要设置 Content-Type 请求头success: function (res) {if (res.code 200) {//修改图像var url url( res.data );jQuery(#i).css(background-image, url);jQuery(#i).val();} else {console.log(上传失败: res.msg);}},error: function () {console.log(上传失败请重试);},});
});2.1.5 测试 2.2 注册
2.2.1 期待效果
输入必选项昵称密码确认密码
确认密码在发送之前进行验证因为没有确认密码这个字段也没有必要这个不需要多说
代码仓库以及头像为非必选
注册成功后弹框提示自动生成的用户名跳转到登录页面并帮助用户填写用户名
而在后端
生成一个加密密码生成一个用户名
2.2.2 约定前后端交互接口
后端
/user/register返回受影响行数以及用户名
前端
bodyimagenamepasswordgitpostjson/user/register
2.2.3 后端代码
UserUtils的获取用户名的方法
public static String getUsername() {// 获取当前时间戳long timestamp System.currentTimeMillis();// 生成随机数Random random new Random();int randomNumber random.nextInt(100);// 结合时间戳和随机数生成唯一标识符String identifier String.valueOf(timestamp) String.valueOf(randomNumber);return identifier;
}不适用UUID是因为UUID太长了没啥规律带字母而这里我用的是当前时间戳 100以内的随机数组成的15位十进制数
如果恶意注册用户名才可能重复由于有unique约束所以会添加失败受影响行数返回0~
controller层
RequestMapping(register)
public CommonResult register(RequestBody UserInfo userInfo) {// 1. 校验参数if(userInfo null || !StringUtils.hasLength(userInfo.getName())|| !StringUtils.hasLength(userInfo.getPassword())) {return CommonResult.fail(-1, 非法参数);}// 2. 生成一个用户名String username UserUtils.getUsername();userInfo.setUsername(username);// 3. 加密userInfo.setPassword(UserUtils.encrypt(userInfo.getPassword()));// 4. 请求service的添加数据库操作int rows userService.register(userInfo);// 5. 执行结果返回MapString, Object map new HashMap();map.put(rows, rows);map.put(username, username);return CommonResult.success(map);
}补充 判断字符串为空字符串/null是的话返回false mapper层
实现
由于我需要用到标签所以注解的方式不太方便我用xml去实现
?xml version1.0 encodingUTF-8?
!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.example.demo.mapper.UserMapper/mapperregister实现非必选项的判断
insert idregisterinsert into userinfo (username,name,if testphoto ! nullphoto,/ifif testgit ! nullgit,/ifpassword) values (#{username},#{name},if testphoto ! null#{photo},/ifif testgit ! null#{git},/if#{password})
/insertservice层
Autowired
private UserMapper userMapper; public int register(UserInfo userInfo) {return userMapper.register(userInfo);
}拦截器配置 2.2.4 前端代码
用户名不能全为空并且上传时空白符会被去除掉密码不能全为空并且上传时空白符会被去除掉 实现 function register() {var name jQuery(#username);var password jQuery(#password);var judge_password jQuery(#judge_password);var photo jQuery(#i).css(background-image).replace(url(, ).replace(), ).replace(\, ).replace(\, );//去掉两个引号var git jQuery(#git);// 1. 非空校验if (name.val().trim() ) {alert(请输入昵称);name.focus();return false;}if (password.val().trim() ) {alert(请输入密码);password.focus();return false;}if (judge_password.val().trim() ) {alert(请输入密码);judge_password.focus();return false;}if (password.val() ! judge_password.val()) {alert(两次输入密码不一致);return false;}// 2. 发送请求jQuery.ajax({url: /user/register,method: POST,contentType: application/json; charsetutf8,data: JSON.stringify({name: name.val().trim(),password: password.val().trim(),photo: photo,git: git.val(),}),// 3. 处理响应success: function (body) {if (body.code 200 body.data.rows 1) {alert(注册成功请记住你的用户名 body.data.username );location.href blog_login.html?username body.data.username;} else {alert(注册失败 data.msg);}},});
}2.2.5 测试
在这里插入图片描述
3. 博客登录页
3.1 期待效果 用户名输入框聚焦到失焦的时候发送请求给后端尝试获取用户头像根据querystring如果有用户名将用户名进行赋值两个输入框都输入值后才能发送登录请求后端对数据进行校验点击注册按钮跳转到注册页
3.2 失焦更新头像
3.2.1 约定前后端交互接口
后端
/user/get_photo返回头像名
前端
/user/get_photo用户名json
3.2.2 后端代码
controller层
RequestMapping(/get_photo)
public CommonResult getPhoto(RequestBody UserInfo user) {String username user.getUsername();UserInfo userInfo userService.getUserByUsername(username);return userInfo ! null ? CommonResult.success(userInfo.getPhoto()) : CommonResult.fail(-1, 没有此用户);
}service层
public UserInfo getUserByUsername(String username) {return userMapper.selectByUsername(username);
}mapper层
Select(select * from userinfo where username #{username})
UserInfo selectByUsername(Param(username) String username);拦截器配置
.excludePathPatterns(/user/get_photo)
3.2.3 前端代码 给用户名输入框一个失焦事件
jQuery(#username).blur(function () {var username jQuery(#username);if (username.val().trim() ! ) {jQuery.ajax({url: /user/get_photo,method: post,contentType: application/json; charsetutf8,data: JSON.stringify({username: username.val().trim(),}),success: function (body) {if (body.code 200 body.data ! ) {var img url( body.data );jQuery(#i).css(background-image, img);} else {jQuery(#i).css(background-image,url(blog_userImage/avatar.png));}},});}
});3.2.4 测试 3.3 处理url 以及 注册页面跳转
3.3.1 通过key获取url中的value
script srcjs/url_handler.js/script// 根据 key 获取 url 中对应的 value
function getParamValue(key){// 1.得到当前url的参数部分var params location.search;// 2.去除“?”if(params.indexOf(?)0){params params.substring(1);// 3.根据“”将参数分割成多个数组var paramArray params.split();// 4.循环对比 key并返回查询的 valueif(paramArray.length1){for(var i0;iparamArray.length;i){// keyvaluevar item paramArray[i].split();if(item[0]key){return item[1];}}}}return null;
}3.3.2 将username赋值给用户名输入框
jQuery(#username).val(getParamValue(username));
jQuery(#username).focus();3.3.3 注册页面跳转 3.3.4 测试 3.4 登录功能
3.4.1 约定前后端交互接口
后端
/user/login返回 true / false
前端
/user/login用户名密码jsonposttrue跳转到所有人的博客列表页false弹窗提示
3.4.2 后端代码
controller层 RequestMapping(/login)
public CommonResult login(RequestBody UserInfo userInfo, HttpServletRequest request) {//1. 参数校验if(userInfo.getUsername() null || !StringUtils.hasLength(userInfo.getUsername())|| userInfo.getPassword() null || !StringUtils.hasLength(userInfo.getPassword())) {return CommonResult.fail(-1, 非法参数);}//2. 根据用户名查询对象UserInfo user userService.getUserByUsername(userInfo.getUsername());if(user null || user.getId() 0) {return CommonResult.fail(-2, 用户名或者密码错误);}//3. 验证密码左边待测右边数据库查出来的if(!UserUtils.confirm(userInfo.getPassword(), user.getPassword())) {return CommonResult.fail(-2, 用户名或者密码错误);}//4. 比较成功将对象存储到session中SessionUtils.setUser(request, user);//5. 返回结果return CommonResult.success(登录成功);
}拦截器配置
.excludePathPatterns(/user/login)
3.4.3 前端代码 function login() {var username jQuery(#username);var password jQuery(#password);// 1. 非空校验if (username.val().trim() ) {alert(请输入昵称);username.focus();return false;}if (password.val().trim() ) {alert(请输入密码);password.focus();return false;}// 2. 发送请求jQuery.ajax({url: /user/login,method: POST,contentType: application/json; charsetutf8,data: JSON.stringify({username: username.val().trim(),password: password.val().trim(),}),// 3. 处理响应success: function (body) {if (body.code 200) {alert(登录成功);location.href blog_lists.html;} else {alert(登录失败 body.msg);}},});
}3.4.4 测试 可以访问需要登录校验的页面 文章到此结束谢谢观看 可以叫我 小马我可能写的不好或者有错误但是一起加油鸭 代码myblog_system · 游离态/马拉圈2023年9月 - 码云 - 开源中国 (gitee.com)