基于Spring实现博客项目
访问地址:用户登录
代码获取:基于Spring实现博客项目: Spring项目写博客项目
一.项目开发
1.项目开发阶段
- 需求评审,需求分析
- 项目设计(接口设计,DB设计等,比较大的需求,需要设计流程图,用例图,UML, model中的字段)
- 开发+自测
- 提测(提交测试)
- 验收:预发布环境/预生产环境部署测试(开发,测试,产品)
- 上线
2.前端页面
1.登录页面: 根据用户名和密码,进行登录
2.博客列表页:显示所有博客列表,以及显示登录用户的个人信息 3.博客详情页:显示当前博客的详细信息,以及作者信息
4.博客插入/编辑页
3.需求分析
1.登录接口(根据用户名和密码,来判断是否登录成功) 2.根据用户ID,获取用户相关信息
3.获取所有的博客列表
4.根据博客ID,获取博客的详情信息(作者ID) 5.根据博客ID,更新博客内容
6.插入博客
7.删除博客
4.数据库设计
--建表sqlcreate database if not exists `java_blog_spring` charset utf8mb4;--用户表drop table if exists `java_blog_spring`.`user`;create table `java_blog_spring`.`user` (`id` int not null auto_increment,`user_name` varchar(128) not null,`password` varchar(128) not null,`github_url` varchar(128) null,`delete_flag` tinyint(4) null default 0,`create_time` timestamp null default current_timestamp(),primary key (`id`),unique index `user_name_unique` (`user_name` asc))engine = innodb default character set = utf8mb4 comment = '⽤户表';--博客表drop table if exists `java_blog_spring`.`blog`;create table `java_blog_spring`.`blog` (`id` int not null auto_increment,`title` varchar(200) null,`content` text null,`user_id` int(11) null,`delete_flag` tinyint(4) null default 0,`create_time` timestamp null default current_timestamp(),primary key (`id`))engine = innodb default charset = utf8mb4 comment = '博客表';--新增用户信息insert into `java_blog_spring`.`user` (`user_name`, `password`,`github_url`)values("zhangsan","123456","https://gitee.com/bubble-fish666/class-java45");insert into `java_blog_spring`.`user` (`user_name`, `password`,`github_url`)values("lisi","123456","https://gitee.com/bubble-fish666/class-java45");insert into `java_blog_spring`.`blog` (`title`,`content`,`user_id`) values("第一篇博客","111我是博客正文我是博客正文我是博客正文",1);insert into `java_blog_spring`.`blog` (`title`,`content`,`user_id`) values("第二篇博客","222我是博客正文我是博客正文我是博客正文",2);
二.Java项目
1.创建Spring项目
创建好之后为以下页面
2.创建实体类
用户实体类
@Data
public class User {private Integer id;private String userName;private String password;private String githubUrl;private Byte deleteFlag;private Date createTime;
}
博客实体类
@Data
public class Blog {private Integer id;private String title;private String content;private Integer userId;private Byte deleteFlag;private Date createTime;
}
3.创建Mapper
userMapper.java
@Mapper
public interface UserMapper {@Select("select * from user where id=#{id}")User selectById(Integer id);@Select("select * from user where user_name=#{name}")User selectByName(String name);
}
userMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.javastudy.blog_spring.mapper.UserMapper"></mapper>
blogMapper.java
@Mapper
public interface BlogMapper {@Select("select * from blog where delete_flag=0")List<Blog> selectAll();@Select("select * from blog where id=#{id} and delete_flag=0")Blog selectById(Integer id);Integer updateBlog(Blog blog);@Insert("insert into blog(content,title,user_id) values(#{content},#{title},#{userId})")Integer insertBlog(Blog blog);
}
blogMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.javastudy.blog_spring.mapper.BlogMapper"><update id="updateBlog">update blog<set><if test="title!=null">title=#{title},</if><if test="content!=null">content=#{content},</if><if test="userId!=null">user_id=#{userId},</if><if test="deleteFlag!=null">delete_flag=#{deleteFlag},</if></set>where id=#{id};</update></mapper>
4.配置文件
application.yml
spring:profiles:active: dev# 日志信息
logging:file:path: logs/level:root: info
application-dev.yml
server:port: 8080# 数据库连接配置
spring:datasource:url: jdbc:mysql://localhost:13306/java_blog_spring?characterEncoding=utf8&useSSL=falseusername: rootpassword: woaini520driver-class-name: com.mysql.cj.jdbc.Driver# mybatis xml 配置路径
mybatis:mapper-locations: classpath:mapper/**Mapper.xmlconfiguration: # 配置打印 MyBatis 执行的 SQLlog-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true #驼峰转换
application-prod.yml
server:port: 8080# 数据库连接配置
spring:datasource:url: jdbc:mysql://localhost:3306/java_blog_spring?characterEncoding=utf8&useSSL=falseusername: rootpassword: woaini520driver-class-name: com.mysql.cj.jdbc.Driver# mybatis xml 配置路径
mybatis:mapper-locations: classpath:mapper/**Mapper.xmlconfiguration:map-underscore-to-camel-case: true
5.测试类
userMapper
@SpringBootTest
@Slf4j
class UserMapperTest {@AutowiredUserMapper userMapper;@Testvoid selectById() {User user = userMapper.selectById(1);log.info(user.toString());}@Testvoid selectByName() {User user = userMapper.selectByName("zhangsan");log.info(user.toString());}
}
blogMapper
@SpringBootTest
@Slf4j
class BlogMapperTest {@AutowiredBlogMapper blogMapper;@Testvoid selectAll() {List<Blog> blogs = blogMapper.selectAll();log.info(blogs.toString());}@Testvoid selectById() {Blog blog = blogMapper.selectById(1);log.info(blog.toString());}@Testvoid updateBlog() {Blog blog = new Blog();blog.setId(1);blog.setTitle("测试的第一篇博客");blog.setTitle("测试的第一篇博客的正文内容");blogMapper.updateBlog(blog);}@Testvoid insertBlog() {Blog blog = new Blog();blog.setTitle("第三篇博客");blog.setContent("第三篇博客的正文内容");blog.setUserId(1);blogMapper.insertBlog(blog);}
}
6.Common包
1.Result
@Data
public class Result {//业务处理状态码 200成功 <=0表示失败 (注意与请求状态码区分)private Integer code;//业务返回信息private String msg;//业务数据private Object data;/*** 业务处理失败** @param code* @param message* @return*/public static Result fail(Integer code, String message) {Result result = new Result();result.setCode(code);result.setMsg(message);result.setData("");return result;}public static Result fail(Integer code, String message, Object data) {Result result = new Result();result.setCode(code);result.setMsg(message);result.setData(data);return result;}public static Result success(Object data) {Result result = new Result();result.setCode(200);result.setMsg("");result.setData(data);return result;}public static Result success(String message, Object data) {Result result = new Result();result.setCode(200);result.setMsg(message);result.setData(data);return result;}}
2.ErrorAdvice(统一异常返回)
@ControllerAdvice
public class ErrorAdvice {@ExceptionHandlerpublic Result error(Exception e) {return Result.fail(-1, e.getMessage());}
}
3.统一返回格式处理
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {if (body instanceof Result) {return body;}if (body instanceof String) {ObjectMapper objectMapper = new ObjectMapper();return objectMapper.writeValueAsString(Result.success(body));}return Result.success(body);}}
7.导入前端代码
前端代码在这取:基于Spring实现博客项目: Spring项目写博客项目
三.业务代码
1.约定前后端交互接口
[请求]
/blog/getlist
[响应]
[
{
blogId: 1,
title: "第⼀篇博客",
content: "博客正⽂",
userId: 1,
postTime: "2021-07-07 12:00:00"
},
{
blogId: 2,
title: "第⼆篇博客",
content: "博客正⽂",
userId: 1,
postTime: "2021-07-07 12:10:00"
},
2.Service层
UserService
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate BlogMapper blogMapper;public User selectById(Integer id) {return userMapper.selectById(id);}public User selectByName(String name) {return userMapper.selectByName(name);}public User getAuthorInfoByBlogId(Integer id) {Blog blog = blogMapper.selectById(id);if (blog == null || blog.getUserId() < 0) {return null;}return userMapper.selectById(blog.getUserId());}
}
BlogService
@Service
public class BlogService {@Autowiredprivate BlogMapper blogMapper;public List<Blog> getAll() {return blogMapper.selectAll();}public Blog getBlogById(Integer id) {return blogMapper.selectById(id);}public Integer updateBlog(Blog blog) {return blogMapper.updateBlog(blog);}public Integer addBlog(Blog blog) {return blogMapper.insertBlog(blog);}}
3.Controller层
BlogController
@RestController
@RequestMapping("/blog")
public class BlogController {@AutowiredBlogService blogService;@RequestMapping("/getlist")public List<Blog> getBlogList() {return blogService.getAll();}@RequestMapping("/getBlogDetail")public Result getBlogDetail(Integer id, HttpSession httpSession) {if (id == null || id < 0) {return Result.fail(-1, "非法的参数");}Blog blog = blogService.getBlogById(id);User user = (User) httpSession.getAttribute(Constants.USER_INFO_SESSION);if (blog.getUserId() == user.getId()) {blog.setIsLoginUser(true);}return Result.success(blog);}@RequestMapping("/add")public Result addBlog(String title, String content, HttpSession httpSession) {if (!StringUtils.hasLength(title) || !StringUtils.hasLength(content)) {return Result.fail(-1, "内容不能为空");}User user = (User) httpSession.getAttribute(Constants.USER_INFO_SESSION);if (user == null || user.getId() < 0) {return Result.fail(-1, "用户不存在");}Blog blog = new Blog();blog.setContent(content);blog.setTitle(title);blog.setUserId(user.getId());blogService.addBlog(blog);return Result.success(true);}@RequestMapping("/update")public Result updateBlog(Blog blog) {if (!StringUtils.hasLength(blog.getContent()) || !StringUtils.hasLength(blog.getTitle()) || blog.getId() == null) {return Result.fail(-1, "内容不能为空");}blogService.updateBlog(blog);return Result.success(true);}@RequestMapping("/delete")public Result deleteBlog(@RequestParam("id") Integer blogId) {if (blogId == null || blogId < 0) {return Result.fail(-1, "博客id不合法或者为空");}Blog blog = new Blog();blog.setId(blogId);blog.setDeleteFlag((byte) 1);blogService.updateBlog(blog);return Result.success(true);}
}
UserController
@RestController
@RequestMapping("/user")
public class UserController {@AutowiredUserService userService;@RequestMapping("/login")public Result login(HttpServletRequest request, String username, String password) {//参数校验if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return Result.fail(-2, "账号或密码不能为空");}//密码校验User user = userService.selectByName(username);if (user == null || !user.getPassword().equals(password)) {return Result.fail(-3, "用户名不存在或者密码错误");}//参数返回user.setPassword("");HttpSession session = request.getSession(true);session.setAttribute(Constants.USER_INFO_SESSION, user);return Result.success("登陆成功");}@RequestMapping("/getUserInfo")public Result getUserInfo(HttpSession session) {if (session == null || session.getAttribute(Constants.USER_INFO_SESSION) == null) {return Result.fail(-1, "用户未登录");}return Result.success(session.getAttribute(Constants.USER_INFO_SESSION));}@RequestMapping("/getAuthorInfo")public Result getAuthorInfoByBlogId(@RequestParam("id") Integer blogId) {if (blogId == null || blogId < 0) {return Result.fail(-1, "id参数错误");}User user = userService.getAuthorInfoByBlogId(blogId);if (user == null) {return Result.fail(-1, "不存在文章作者");}user.setPassword("");return Result.success(user);}@RequestMapping("/logout")public Result logout(HttpSession session) {session.removeAttribute(Constants.USER_INFO_SESSION);return Result.success(true);}
}
4.登录接口拦截器
@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HttpSession session = request.getSession(false);if (session == null || session.getAttribute(Constants.USER_INFO_SESSION) == null) {response.setStatus(401);return false;}return true;}
}
@Configuration
public class AppConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;private final List<String> excludes = Arrays.asList("/**/*.html","/blog-editormd/**","/css/**","/js/**","/pic/**","/user/login");@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**"). //拦截所有路径excludePathPatterns(excludes);}
}
在blog_list.html和blog_detail的ajax请求添加
error: function (error) {if (error != null && error.status == 401) {location.assign("/blog_login.html");}}
四.前端代码
有些代码重复出现,可以抽象为一个函数,我们放在./js/common.js中
function logout() {$("#logout").click(function () {$.ajax({type: "get",url: "/user/logout",success: function (result) {if (result != null && result.data == true) {location.assign("/blog_login.html")}},})})
}
1.blog_list.html
先来个前置知识:日期格式化
方式一:将日期格式改为(java.sql.Date适用)
private Timestamp createTime;
然后引入js包
function formatDate(time) {var date = new Date(time);var year = date.getFullYear(),month = date.getMonth() + 1,//月份是从0开始的day = date.getDate(),hour = date.getHours(),min = date.getMinutes(),sec = date.getSeconds();var newTime = year + '-' +(month < 10 ? '0' + month : month) + '-' +(day < 10 ? '0' + day : day) + ' ' +(hour < 10 ? '0' + hour : hour) + ':' +(min < 10 ? '0' + min : min) + ':' +(sec < 10 ? '0' + sec : sec);return newTime;
}
直接进行转化即可
<script src="./js/common.js"></script>
formatDate(blog.createTime)
方式二:(java.util.Date适用)
工具类
public class DateUtils {public static String formatDate(Date date) {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return simpleDateFormat.format(date);}
}
博客实体类
@Data
public class Blog {private Integer id;private String title;private String content;private Integer userId;private Byte deleteFlag;private Date createTime;public String getCreateTime() {return DateUtils.formatDate(createTime);}
}
此时就不需要多余的操作了
finalHtml += '<div class="date">'+blog.createTime+'</div>';
之后javascrip代码
<script src="./js/jquery.min.js"></script><script src="./js/common.js"></script><script>$(function () {$.ajax({type: "get",url: "/blog/getlist",success: function (result) {var finalHtml = "";if (result.code == 200 && result.data != null && result.data.length > 0) {var dataHtml = result.data;for (var i = 0; i < dataHtml.length; ++i) {var blog = dataHtml[i];finalHtml += '<div class="blog">';finalHtml += '<div class="title">' + blog.title + '</div>';finalHtml += '<div class="date">' + blog.createTime + '</div>';finalHtml += '<div class="desc">' + blog.content + '</div>';finalHtml += '<a class="detail" href="blog_detail.html?id=' + blog.id + '">查看全文>></a>';finalHtml += '</div>';}$(".right").html(finalHtml);}},error: function (error) {if (error != null && error.status == 401) {location.assign("/blog_login.html");}}});$.ajax({type: "get",url: "/user/getUserInfo",success: function (result) {if (result != null && result.code == 200 && result.data != null) {$(".container .left .card h3").text(result.data.userName);$(".container .left .card a").attr("href", result.data.githubUrl)}},error: function (error) {if (error != null && error.status == 401) {location.assign("/blog_login.html");}}});logout();})</script>
2.blog_detail.html
<script src="./js/jquery.min.js"></script><script src="blog-editormd/editormd.js"></script><script src="blog-editormd/lib/marked.min.js"></script><script src="blog-editormd/lib/prettify.min.js"></script><script src="js/common.js"></script><script>$(function () {$.ajax({type: "get",url: "/blog/getBlogDetail" + location.search,success: function (result) {if (result.code == 200 && result.data != null) {var blog = result.data;$(".title").text(blog.title);$(".date").text(blog.createTime);editormd.markdownToHTML("content", {markdown: blog.content,});if (result.data.isLoginUser == true) {var innerhtml = "";innerhtml += '<button onclick="window.location.href = \'blog_update.html?id=' + blog.id + '\'">编辑</button>';innerhtml += '<button onclick="deleteBlog()">删除</button>';$(".operating").html(innerhtml);}}},error: function (error) {if (error != null && error.status == 401) {location.assign("/blog_login.html");}}});$.ajax({type: "get",url: "/user/getAuthorInfo" + location.search,success: function (result) {if (result != null && result.code == 200 && result.data != null) {$(".container .left .card h3").text(result.data.userName);$(".container .left .card a").attr("href", result.data.githubUrl)}},error: function (error) {if (error != null && error.status == 401) {location.assign("/blog_login.html");}}});// jQuery("#deleteButton").click(function () {// if (confirm("确定要删除吗?")) {// $.ajax({// type: "post",// url: "/blog/delete" + location.search,// success: function (result) {// if (result != null && result.data == true) {// location.assign("blog_list.html");// }// }// })// }// });})function deleteBlog() {if (confirm("确定要删除吗?")) {$.ajax({type: "post",url: "/blog/delete" + location.search,success: function (result) {if (result != null && result.data == true) {location.assign("blog_list.html");}}})}};logout();</script>
3.blog_login.html
<script src="./js/jquery.min.js"></script><script>$(function () {$("#submit").click(function () {//获取用户名var username = jQuery("#username");if (!username.val()) {alert("用户名不能为空");username.focus();return;}//获取密码var password = jQuery("#password");if (!password.val()) {alert("密码不能为空");password.focus();return;}$.ajax({type: "post",url: "/user/login",data: {username: username.val(),password: password.val()},success: function (result) {if(result.code==200){//跳转到主页location.href="blog_list.html";return;}else{//业务处理失败alert(result.msg);return;}},error: function () {}})})})</script>
4.blog_edit.html
<script src="js/jquery.min.js"></script><script src="blog-editormd/editormd.min.js"></script><script type="text/javascript">$(function () {var editor = editormd("editor", {width: "100%",height: "550px",path: "blog-editormd/lib/"});$("#submit").click(function () {$.ajax({type: "post",url: "/blog/add",data: {title: $("#title").val(),content: $("#content").val()},success: function (result) {if (result != null && result.data != null && result.data == true) {location.assign("blog_list.html");}},error: function (result) {if (result != null && result.status == 401) {alert("请登录之后再进行操作");}}})})});</script>
5.blog_update.html
<script src="js/jquery.min.js"></script><script src="blog-editormd/editormd.min.js"></script><script src="js/common.js"></script><script type="text/javascript">$(function () {var editor = editormd("editor", {width: "100%",height: "550px",path: "blog-editormd/lib/"});$.ajax({type: "get",url: "/blog/getBlogDetail" + location.search,success: function (result) {var blog = result.data;if (result != null && result.code == 200 && result.data != null) {$("#title").val(blog.title);$("#content").val(blog.content);$("#blogId").val(blog.id);}},error: function (result) {if (result != null && result.status == 401) {alert("请登录之后再进行操作");}}});$("#submit").click(function () {$.ajax({type: "post",url: "/blog/update",data: {title: $("#title").val(),content: $("#content").val(),id: $("#blogId").val()},success: function (result) {if (result != null && result.data == true) {location.assign("blog_list.html");}},error: function (result) {if (result != null && result.status == 401) {alert("请登录之后再进行操作");}}})})});</script>
五.加密措施
1.加密
假设我们的数据库被黑客进行劫持,那么用户的一些重要信息(比如密码,身份证号)就可能被获取,这样对用户是十分不利的,因此我们需要对数据库中一些重要的信息进行加密存储.
目前有许多的加密算法,包括可逆加密和非可逆加密,可逆加密在http和https的时候就有使用,通过密钥就可以使明文变为密文,同时也可以让密文变为明文,但是非可逆加密就不同了,非可逆加密只能使明文加密为密文,不能从密文变为明文,一般在数据库中我们采用非可逆加密.
常用的非可逆加密有MD5加密,通过对明文加密,可以得到长度固定的MD5值,MD5加密就可以得到32位或者16位的加密后的结果.
Java中使用MD5加密
@SpringBootTest
class BlogSpringApplicationTests {@Testvoid contextLoads() {String finalPassword = DigestUtils.md5DigestAsHex("123456".getBytes());System.out.println("第一次加密:" + finalPassword);finalPassword = DigestUtils.md5DigestAsHex("123456".getBytes());System.out.println("第二次加密:" + finalPassword);finalPassword = DigestUtils.md5DigestAsHex("123456".getBytes());System.out.println("第三次加密:" + finalPassword);}}
可以观察到:MD5加密后得到的值是都是一样的,并且无论进行多少次MD5加密(对产生的MD5值多次进行加密),得到的结果都是一样的.
那么这样会不会产生安全问题呢?当然会!虽然说MD5加密是不可逆的加密,但是可以通过提前生成对应的明文和密文对应的表,然后通过需要破解的密文与之前生成表的密文进行对应,从而找到相应的明文,但是这种方法是十分低效的,但是也可以进行破解明文,尤其对于密码不复杂且短的很容易破解,那么怎么样可以避免这种情况呢?
首先可以在用户端进行,我们在设置的密码的时候,通常网站都要求需要字母与数字的组合,并且长度都要求大于一定的值,这样密码的安全等级很高,不容易被破解,(比如纯数字组合就10种,字母加数字就有26+10种,大小写字母加数字组合就有26*2+10种,长度越长组合自然也就越多).
接下来通过服务端通过加盐的方式存储密码.
2.盐值
假设用户的密码安全很低,如果在存储的时候将密码与随机的字符串的进行拼接,然后再通过MD5加密的方式进行存储的话,同样也可以时密码难以破解,并且可以通过生成多个不同的随机字符串,可以解决MD5不能实现多次加密的问题.随机生成的字符串也要存储到数据库中,通常与生成的MD5数值拼接存储.
代码展示,通过UUID生成随机的字符串.
@Testvoid contextLoads() {String salt = UUID.randomUUID().toString();String finalPassword = DigestUtils.md5DigestAsHex(("123456" + salt).getBytes());System.out.println("第一次salt:" + salt);System.out.println("第一次加密:" + finalPassword);salt = UUID.randomUUID().toString();finalPassword = DigestUtils.md5DigestAsHex(("123456" + salt).getBytes());System.out.println("第二次salt:" + salt);System.out.println("第二次加密:" + finalPassword);salt = UUID.randomUUID().toString();finalPassword = DigestUtils.md5DigestAsHex(("123456" + salt).getBytes());System.out.println("第三次salt:" + salt);System.out.println("第三次加密:" + finalPassword);}
我们不希望产生的salt含有特殊字符,因此我们可以通过以下的方式将"-"去掉
@Testvoid contextLoads() {String salt = UUID.randomUUID().toString().replace("-", "");String finalPassword = DigestUtils.md5DigestAsHex(("123456" + salt).getBytes());System.out.println("第一次salt:" + salt);System.out.println("第一次加密:" + finalPassword);salt = UUID.randomUUID().toString().replace("-", "");finalPassword = DigestUtils.md5DigestAsHex(("123456" + salt).getBytes());System.out.println("第二次salt:" + salt);System.out.println("第二次加密:" + finalPassword);salt = UUID.randomUUID().toString().replace("-", "");finalPassword = DigestUtils.md5DigestAsHex(("123456" + salt).getBytes());System.out.println("第三次salt:" + salt);System.out.println("第三次加密:" + finalPassword);}
3.实现
public class SecurityUtils {/*** 将密码进行加密* 根据明文,返回密文(salt+MD5)** @param password* @return*/public static String encry(String password) {//生成盐值String salt = UUID.randomUUID().toString().replace("-", "");//进行加密String finalPassword = DigestUtils.md5DigestAsHex((password + salt).getBytes());return salt + finalPassword;}/*** @param inputPassword 输入的密码* @param finalPassword 数据库存储的密码* @return 输入的密码和存储的密码是否相同*/public static boolean decrypt(String inputPassword, String finalPassword) {if (!StringUtils.hasLength(inputPassword) || !StringUtils.hasLength(finalPassword)) {return false;}if (finalPassword.length() != 64) {return false;}String salt = finalPassword.substring(0, 32);String password = DigestUtils.md5DigestAsHex((inputPassword + salt).getBytes());return (salt + password).equals(finalPassword);}}
修改密码验证的接口
@RequestMapping("/login")public Result login(HttpServletRequest request, String username, String password) {//参数校验if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return Result.fail(-2, "账号或密码不能为空");}//密码校验User user = userService.selectByName(username);if (user == null || !SecurityUtils.decrypt(password, user.getPassword())) {return Result.fail(-3, "用户名不存在或者密码错误");}//参数返回user.setPassword("");HttpSession session = request.getSession(true);session.setAttribute(Constants.USER_INFO_SESSION, user);return Result.success("登陆成功");}
六.上线发布
1.多平台开发
<profiles><profile><id>dev</id><properties><env>dev</env></properties><activation><activeByDefault>true</activeByDefault></activation></profile><profile><id>prod</id><properties><env>prod</env></properties><activation><activeByDefault>false</activeByDefault></activation></profile></profiles>
如果需要跳过test,可以点击
application.yml文件
此时打包即可
2.部署linux服务器
1.创建数据库
可以将sql语句变成一个文件,然后执行下面的
source /root/java78/create.sql
2.将代码打包
打包完成之后,将jar包拖拽到linux服务器上
3.运行代码
后台启动项目
nohup java -jar Blog_Spring-0.0.1-SNAPSHOT.jar &
查看日志
cd logs/
tail -f spring.log
终止当前的服务
ps -ef | grep [ ]
注意:如果开启多个服务,需要开端口,给防火墙添加端口号
查看防火墙状态(如果没开启,建议开启,不开启可以直接访问,开启了需要进行已下的操作访问)
systemctl status firewalld
启动防火墙和关闭防火墙
systemctl start firewalld
systemctl stop firewalld
查看开放的端口号
firewall-cmd --list-ports
开启8080端口
firewall-cmd --permanent --add-port=8080/tcp
重启防火墙
firewall-cmd --reload
设置开机启动
systemctl enable firewalld
添加安全组
否则无法正常访问
相关文章:

基于Spring实现博客项目
访问地址:用户登录 代码获取:基于Spring实现博客项目: Spring项目写博客项目 一.项目开发 1.项目开发阶段 需求评审,需求分析项目设计(接口设计,DB设计等,比较大的需求,需要设计流程图,用例图,UML, model中的字段)开发+自测提测(提交测试…...

数据库第十七课-------ETL任务调度系统的安装和使用
作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 🎂 作者介绍: 🎂🎂 🎂 🎉🎉🎉…...
Qt 动态中英文切换
背景: 需要界面实现动态国际化,一键点击切换中英文或其他语言。 前提: 已经完成了整个界面的翻译,拿到匹配的ts翻译文件,注意:要保证界面切换后,翻译的全覆盖,要保证任何需要反应的地方,都用到了tr("")包含,不然Linguist会捕捉不到。.ts文件的生成参考下文…...

hdfs操作
hadoop fs [generic options] [-appendToFile … ] [-cat [-ignoreCrc] …] [-checksum …] [-chgrp [-R] GROUP PATH…] [-chmod [-R] <MODE[,MODE]… | OCTALMODE> PATH…] [-chown [-R] [OWNER][:[GROUP]] PATH…] [-copyFromLocal [-f] [-p] [-l] [-d] … ] [-copyTo…...

h5分享页适配手机电脑
实现思路 通过media媒体查询结合rem继承html文字大小来实现。 快捷插件配置 这里使用了VSCode的px to rem插件。 先在插件市场搜索cssrem下载插件; 配置插件 页面编写流程及适配详情 配置meta h5常用配置信息:<meta name"viewport" content&quo…...
崭新商业理念:循环购模式的价值引领-微三云门门
尊敬的创业者们,我是微三云门门,今天我将为您详细探讨一种具有颠覆性的商业模式——循环购模式。这套私域流量裂变策略在实际应用中取得了巨大的成功,某些企业在短短6个月内迅速积累了400万用户! 循环购商业模式的核心聚焦于三个…...

二级MySQL(二)——编程语言,函数
SQL语言又称为【结构化查询语言】 请使用FLOOR(x)函数求小于或等于5.6的最大整数 请使用TRUNCATE(x,y)函数将数字1.98752895保留到小数点后4位 请使用UPPER()函数将字符串‘welcome’转化为大写…...

python爬虫12:实战4
python爬虫12:实战4 前言 python实现网络爬虫非常简单,只需要掌握一定的基础知识和一定的库使用技巧即可。本系列目标旨在梳理相关知识点,方便以后复习。 申明 本系列所涉及的代码仅用于个人研究与讨论,并不会对网站产生不好…...

系列十三、idea创建文件自动生成作者信息
File>Settings>Editor>File and Code Templates>Includes>File Header /*** Author : 一叶浮萍归大海* Date: ${DATE} ${TIME}* Description: */...
spring websocket demo
一 java依赖 gradle 配置 implementation "org.springframework.boot:spring-boot-starter-websocket" implementation "org.springframework.security:spring-security-messaging" 二 配置WebSocketConfig import org.springframework.beans.factory.a…...

C语言的发展及特点
1. C语言的发展历程 C语言作为计算机编程领域的重要里程碑,其发展历程承载着无数开发者的智慧和创新。C语言诞生于20世纪70年代初,由计算机科学家Dennis Ritchie在贝尔实验室首次推出。当时,Ritchie的目标是为Unix操作系统开发一门能够更方便…...

Flink Kubernates Native - 入门
创建 namespace [rootCentOSA flink-1.17.1]# kubectl create ns flink-native [rootCentOSA flink-1.17.1]# kubectl config set-context --current --namespaceflink-native命令空间添加资源限制 [rootCentOSA flink-1.17.1]# vim namespace-ResourceQuota.yamlapiVersion:…...
Ceph入门到精通-大流量10GB/s支持OSPF(ECMP)-LVS 集群
Keepalived-LVS 能够提高集群的高可用性并增加后端检测功能、简化配置,满足常规需求。但Keepalived-LVS集群中,同一个VIP只能由一台设备进行宣告,为一主多备的架构,不能横向拓展集群的性能,为此我们引入OSPF来解决该问…...
IDEA、git如何修改历史提交commit的邮箱
第一种情况:当前提交不是从其他分支clone过来的: step1: git log 查看提交日志,获取commit ID step2: git rebase -i [你的commitID] git rebase -i c2ef237854290051bdcdb50ffbdbb78481d254bb step3:…...

字符设备驱动(内核态用户态内存交互)
前言 内核驱动:运行在内核态的动态模块,遵循内核模块框架接口,更倾向于插件。 应用程序:运行在用户态的进程。 应用程序与内核驱动交互通过既定接口,内核态和用户态访问依然遵循内核既定接口。 环境搭建 系统&#…...
Qt基础 线程池
目录 QThreadPool类 QRunnable类 应用场景示例 QThreadPool类 主要属性: 1、activeThreadCount: 此属性表示线程池中的活动线程数,通过activeThreadCount() 调用。 2、expiryTimeout: 线程活着的时间。没有设置expiryTimeout毫秒的线程会自动退出&am…...

Django(8)-静态资源引用CSS和图片
除了服务端生成的 HTML 以外,网络应用通常需要一些额外的文件——比如图片,脚本和样式表——来帮助渲染网络页面。在 Django 中,我们把这些文件统称为“静态文件”。 我们使用static文件来存放静态资源,django会在每个 INSTALLED…...

C++ list模拟实现
list模拟实现代码: namespace djx {template<class T>struct list_node{T _data;list_node<T>* _prev;list_node<T>* _next;list_node(const T& x T()):_data(x),_prev(nullptr),_next(nullptr){}};template<class T,class Ref,class Pt…...

中国建筑出版传媒许少辉博士八一新书乡村振兴战略下传统村落文化旅游设计日京东当当畅销榜自由营九三学
中国建筑出版传媒许少辉博士八一新书乡村振兴战略下传统村落文化旅游设计日京东当当畅销榜自由营九三学...

C语言(第三十二天)
1. 递归是什么? 递归是学习C语言函数绕不开的一个话题,那什么是递归呢? 递归其实是一种解决问题的方法,在C语言中,递归就是函数自己调用自己。 写一个史上最简单的C语言递归代码: #include <stdio.h>…...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...