基于SSM框架实现的一个博客系统,使用Mybatis-plus进行操作数据库
功能描述
首先进入界面,是一个登录的界面,在输入用户名和密码后登录成功,进入博客列表的页面,在博客列表有查看博客的功能,写博客的功能,注销账户的功能,点击查看博客的功能后,也有编辑博客,删除博客的功能。
功能展示
登录界面
列表页面
写作界面
查看博客界面
编辑博客界面
接下来开始介绍我的项目
项目介绍
数据库的创建
一共有两张表
blog_info表 是博客列表内容的数据库
user_info表 是新增用户的信息
创建的项目
添加Spring MVC和Mybatis-plus的依赖
注:我们这个项目使用的是Mybatis-plus,所以创建好项目记得添加其依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
创建项目的结构目录
配置配置文件
由于我们后面需要将项目上传到服务器中,所以配置三个配置文件
包括:
application.yml(主配置文件):用来存放通常的配置
记得在pom文件中进行配置
<profiles>
<profile>
<id>dev</id>
<properties>
<profile.name>dev</profile.name>
</properties>
</profile>
<profile>
<id>prog</id>
<properties>
<profile.name>prog</profile.name>
</properties>
</profile>
</profiles>
application-dev.yml(开发环境的配置文件):用来存放开放的时候需要的配置文件
application-prod.yml(生产环境的配置文件):用来存放生产的时候需要的配置文件
项目是采用三层架构
分为controller(控制层),service(服务层),mapper(持久层)
首先我们先写common(公共层)的代码
公共层一般是用于存储工具类,配置类
首先定义业务状态的枚举
@AllArgsConstructor:自动生成带参构造器
@Getter
@AllArgsConstructor
public enum ResultCodeEnum {
SUCCESS(200,"操作成功"),
FAIL(-1,"操作失败");
private int code;
private String mes;
}
统一返回结果的封装类
定义了code(业务状态码),errMes(错误信息),data(返回的数据),其中包含三种方法
success方法,再输入成功返回的数据对象后,返回其数据成功的状态码和"操作成功这个信息"
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {
private int code;//业务状态码
private String errMes;
private Object data;
public static Result success(Object data){
Result result=new Result();
result.setData(data);
result.setErrMes("操作成功");
result.setCode(ResultCodeEnum.SUCCESS.getCode());
return result;
}
public static Result fail(String errMes,Object data){
Result result=new Result();
result.setData(data);
result.setErrMes(errMes);
result.setCode(ResultCodeEnum.FAIL.getCode());
return result;
}
public static Result fail(String errMes){
Result result=new Result();
result.setErrMes(errMes);
result.setCode(ResultCodeEnum.FAIL.getCode());
return result;
}
}
最后就是统一返回结果
@RestControllerAdvice是全局的数据处理和异常处理,作用在@RestController注解下的方法返回的数据
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Autowired
private ObjectMapper objectMapper;
//对所有的controller方法执行,因为已经设置为true
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
//在controller返回数据后,在执行这个方法后返回响应体
@SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//如果返回了String类型,进行封装
if(body instanceof String){
return objectMapper.writeValueAsString(Result.success(body));
}
//如果直接返回了Result类型,直接返回,避免重复的包装
if(body instanceof Result){
return body;
}
return Result.success(body);
}
}
定义项目异常BlogException
自定义来处理关于博客业务会出现的异常
@Data
public class BlogException extends RuntimeException{
private String errMsg;
private int code;
public BlogException(String errMsg) {
this.errMsg=errMsg;
}
public BlogException(String errMsg,int code) {
this.code=code;
this.errMsg=errMsg;
}
}
统一异常处理
@ExceptionHandler 用于定义处理某种异常的方法,方法签名
@Slf4j
@RestControllerAdvice
public class ExceptionAdvice {
@ExceptionHandler(Exception.class)
public Result exceptionHandler(Exception e){
// log.error("Exception e:"+e);
return Result.fail(e.getMessage());
}
@ExceptionHandler(BlogException.class)
public Result exceptionHandler(BlogException e){
// log.error("Exception e:"+e);
return Result.fail(e.getErrMsg());
}
@ExceptionHandler(HandlerMethodValidationException.class)
public Result exceptionHandler(HandlerMethodValidationException e){
return Result.fail(e.getMessage());
}
}
业务代码
实体类
记得对照数据库进行创建
博客列表
@Data
public class BlogInfo {
@TableId(value = "id",type = IdType.AUTO) //设置id为主键,并且自增
private Integer id;
private String title;
private String content;
private Integer userId;
private Integer deleteFlag;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
用户列表
@Data
public class UserInfo {
@TableId(value = "id",type = IdType.AUTO) //设置id为主键,并且自增
private Integer id;
private String userName;
private String password;
private String githubUrl;
private Byte deleteFlag;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
持久层 (Mapper)
我们使用的Mybatis-plus完成持久层的开发,创建mapper,记得实现BaseMapper
@Mapper
public interface BlogInfoMapper extends BaseMapper<BlogInfo>{
}
@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {
}
实现博客列表
约定前后端的交互接口
客户端给服务端发送了/blog/getList请求,服务端给客户端返回了Json格式的数据
定义接口返回的实体
@JsonFormat注解,用于控制其中日期的格式
@Data
public class BlogInfoResponse {
private Integer id;
private String title;
private String content;
private Integer userId;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
}
实现图书列表的Controller类
@Slf4j
@RestController
@RequestMapping("/blog")
public class BlogInfoController {
@Resource(name = "blogInfoServiceImpl")
private BlogInfoService blogInfoService;
@RequestMapping("/getList")
public List<BlogInfoResponse> getList(){
return blogInfoService.getList();
}
}
Service采⽤接⼝对外提供服务,实现类⽤Impl的后缀与接⼝区别,好处是让代码更加灵活,起到了解耦的作用
图书列表的service类
public interface BlogInfoService {
List<BlogInfoResponse> getList();
}
@Service
@Slf4j
public class BlogInfoServiceImpl implements BlogInfoService {
@Autowired
private BlogInfoMapper blogInfoMapper;
public List<BlogInfoResponse> getList() {
List<BlogInfo> blogInfos=blogInfoMapper.selectList( //mybatis-plus的查询方法,用户查询满足以下条件的所有记录
new LambdaQueryWrapper<BlogInfo>() //创建一个条件构造器
.eq(BlogInfo::getDeleteFlag, Constants.FLAG_NORMAL).orderByDesc(BlogInfo::getId)); //.eq:条件是
//orderByDesc是将得到的id按desc(降序)排序
//转化格式将BlogInfo格式转化为BlogInfoResponse
return blogInfos.stream().map(blogInfo ->{ //.stream()将得到的博客列表变成一个数据流, .map对每一个数据进行转换
BlogInfoResponse response=new BlogInfoResponse(); //创建一个新的对象
BeanUtils.copyProperties(blogInfo,response); //将blogInfo中的数据复制到response中,
return response; // 这里的blogInfo指的是List<BlogInfo> blogInfos中的每一个数据
}).collect(Collectors.toList()); //最后再将将 Stream 流结果收集成 List
}
}
实现客户端的代码
使⽤ajax给服务器发送HTTP请求
function getBlogList() {
$.ajax({
type: "get",
url: "/blog/getList",
success: function (result) {
//针对后端的结果, 进行简单校验
if (result.code == 200 && result.data != null && result.data.length > 0) {
var finalHtml = "";
for (var blogInfo of result.data) {
finalHtml += '<div class="blog">';
finalHtml += '<div class="title">' + blogInfo.title + '</div>';
finalHtml += '<div class="date">' + blogInfo.createTime + '</div>';
finalHtml += '<div class="desc">' + blogInfo.content + '</div>';
finalHtml += '<a class="detail" href="blog_detail.html?blogId=' + blogInfo.id + '">查看全文>></a>';
finalHtml += '</div>'
}
$(".container .right").html(finalHtml);
}
}
});
}
验证是否能正确的返回数据
http:127.0.0.1:8080/blog/getList
返回结果正确
实现博客详情
约定好前后端交互的接口
BlogController中写getBlogDetail方法,用于获得博客的详情
@RequestMapping("/getBlogDetail")
public BlogInfoResponse getBlogDetail(@NotNull Integer blogId){
log.info("blogId:{}",blogId);
return blogInfoService.getBlogDetail(blogId);
}
记得添加参数校验的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
BlogInfoservice中添加getBlogInfo和getBlogDetail方法
将博客的详情转换为响应对象的原因:是因为数据库的实体类中很多的属性不适合直接给前端,所以定义了一些需要响应的数据,返回给前端
BlogInfoResponse getBlogDetail(Integer blogId);
BlogInfo getBlogInfo(Integer blogId);
@Override
public BlogInfoResponse getBlogDetail(Integer blogId) {
return BeanTranUtils.tran(getBlogInfo(blogId));
}
@Override
public BlogInfo getBlogInfo(Integer blogId) {
QueryWrapper<BlogInfo> queryWrapper=new QueryWrapper<>();
queryWrapper.lambda()
.eq(BlogInfo::getDeleteFlag, Constants.FLAG_NORMAL)
.eq(BlogInfo::getId, blogId);
return blogInfoMapper.selectOne(queryWrapper);
}
实现客户端的代码
使用location.search可得到blogId
getBlogDetail();
function getBlogDetail() {
$.ajax({
type: "get",
url: "/blog/getBlogDetail" + location.search,
success: function (result) {
if (result.code == -1) {
alert(result.errMsg);
return;
}
if (result.code == 200 && result.data != null) {
$(".content .title").text(result.data.title);
$(".content .date").text(result.data.createTime);
$(".content .detail").text(result.data.content);
}
}
});
}
进行验证
返回结果正确
实现登录
使用令牌技术
用户登录进行登录,发送登录请求,经过负载均衡,将请求发给了一台服务器,服务器对其进行账户和密码的验证,验证成功后,生成一个令牌,返回给客户端,客户端收到令牌,将其储存起来,比如说储存在cookie中,用户登录成功后,进行其他功能的方法,此时请求将会发给另一台机器,机器验证其携带的令牌是否有效,如果有效,则说明用户登录成功,如果无效或者为空,则说明用户还未登录。
JWT令牌⽣成和校验
引入JWT的依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<!-- or jjwt-gson if Gson is preferred -->
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
生成令牌
首先生成一个密钥
@Test
public void genKey(){
Key key= Keys.secretKeyFor(SignatureAlgorithm.HS256);
String encode= Encoders.BASE64.encode(key.getEncoded());
System.out.println(encode);
}
生成JWT
我们可以在Https://jwt.io中验证生成的JWT是否正确
约定博客详情的前后端交互的接口
创建JWT⼯具类
public class JwtUtils {
private static final Logger log = LoggerFactory.getLogger(JwtUtils.class);
private static String SECRET_STRING="2FDVor3CaEMpuG4vQ7T6BACe31F7cqPt09OSXEPQlGc=";
private static Key key=Keys.hmacShaKeyFor(Decoders.BASE64.decode(SECRET_STRING));
public static String genJwt(Map<String,Object> chaims){
String jpt=Jwts.builder()
.setClaims(chaims)
.signWith(key)
.compact();
return jpt;
}
public static Claims parseToken(String token){
if(!StringUtils.hasLength(token)){
return null;
}
JwtParser build=Jwts.parserBuilder().setSigningKey(key).build();
Claims claims=null;
try{
claims=build.parseClaimsJws(token).getBody();
}catch (Exception e){
log.error("token解析失败"+token);
}
return claims;
}
}
创建请求的实体类
@Data
public class UserLoginRequest {
@NotNull(message = "用户名不能为空")
@Length(max =20)
private String userName;
@NotNull(message = "密码不能为空")
@Length(min = 2,max =20 )
private String password;
}
创建响应的实体类
@AllArgsConstructor
@Data
public class UserLoginResponse {
private Integer UserId;
private String token;
}
实现Controller
@RequestBody注解的作用是返回的是Json对象
@Validated是用于触发参数校验的机制
因为在请求的实体类中使用了@NotNull注解和@Length注解
@Slf4j
@RestController
@RequestMapping("/user")
public class UserInfoController {
@Resource(name = "userInfoServiceImpl")
private UserInfoService userInfoService;
@RequestMapping("/login")
public UserLoginResponse userLogin(@RequestBody @Validated UserLoginRequest userLoginRequest){
log.info("用户登录 用户:"+userLoginRequest.getUserName());
return userInfoService.userLogin(userLoginRequest);
}
}
实现service
public interface UserInfoService {
UserLoginResponse userLogin(UserLoginRequest userLoginRequest);
}
@Service
@Slf4j
public class UserInfoServiceImpl implements UserInfoService {
@Autowired
private UserInfoMapper userInfoMapper;
@Resource(name = "blogInfoServiceImpl")
private BlogInfoService blogInfoService;
@Override
public UserLoginResponse userLogin(UserLoginRequest userLoginRequest) {
if(userLoginRequest==null){
log.error("请求为空");
throw new BlogException("请求为空");
}
QueryWrapper<UserInfo> queryWrapper=new QueryWrapper<>();
queryWrapper.lambda()
.eq(UserInfo::getUserName,userLoginRequest.getUserName())
.eq(UserInfo::getDeleteFlag, Constants.FLAG_NORMAL);
UserInfo userInfo=userInfoMapper.selectOne(queryWrapper);
if(userInfo==null){
throw new BlogException("用户不存在");
}
if(!SecurityUtils.verify(userLoginRequest.getPassword(),userInfo.getPassword())){
throw new BlogException("请输入正确的密码");
}
Map<String, Object> map=new HashMap<>();
map.put("userId",userInfo.getId());
map.put("userName",userInfo.getUserName());
String token= JwtUtils.genJwt(map);
return new UserLoginResponse(userInfo.getId(),token);
}
实现客户端的代码
function login() {
$.ajax({
type:"post",
url:"/user/login",
contentType: "application/json",
data:JSON.stringify({
userName:$("#username").val(),
password:$("#password").val()
}),
success:function(result){
if(result!=null && result.code == 200){
var response=result.data;
if(!response.token || !response.userId){
alert("登录响应数据不完整,请联系管理员");
return;
}
localStorage.setItem("user_token",response.token);
localStorage.setItem("loginUserId",response.userId);
location.assign("blog_list.html")
}else{
alert("用户名或密码错误");
return ;
}
}
});
}
ajax发⽣异常时,进⾏异常处理,公共处理,可以提取到 common.js
$(document).ajaxError(function(event,xhr,options,exc){
if(xhr.status==400){
alert("参数校验失败");
}
});
实现强制要求登录
添加拦截器
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userToken=request.getHeader(Constants.USER_TOKEN_HEADER_KEY);
if(userToken==null){
response.setStatus(401);
return false;
}
Claims claims= JwtUtils.parseToken(userToken);
if(claims==null){
response.setStatus(401);
return false;
}
return true;
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/user/**","/blog/**")
.excludePathPatterns("/user/login");
}
}
实现客户端的代码
前端请求时,header中统⼀添加token,后端返回异常时,跳转到登录⻚⾯
$(document).ajaxError(function(event,xhr,options,exc){
if(xhr.status==401){
location.assign("blog_login.html");
}else if(xhr.status==400){
alert("参数识别错误");
}
});
$(document).ajaxSend(function(e,xhr,opt){
var user_token=localStorage.getItem("user_token");//从浏览器中的本地存储中得到令牌token
xhr.setRequestHeader("user_token",user_token);//为即将发送的对象添加一个自定义请求头。
});
实现显示用户信息
我们希望这个信息是根据用户登录的变化而变化
约定前后端交互接⼝
定义返回的实体类
@Data
public class UserInfoResponse {
private Integer id;
private String userName;
private String githubUrl;
}
实现controller
@RequestMapping("/getUserInfo")
public UserInfoResponse getUserInfo(@NotNull(message = "userId不能为空") Integer userId){
return userInfoService.getUserInfo(userId);
}
@RequestMapping("/getAuthorInfo")
public UserInfoResponse getBlogInfo(@NotNull(message = "blogId不能为空") Integer blogId){
return userInfoService.getAuthorId(blogId);
}
实现service
UserInfoResponse getUserInfo(Integer userId);
UserInfoResponse getAuthorId(Integer blogId);
首先根据博客Id获得博客的信息,然后根据博客信息获得用户Id,最后根据用户的Id获得用户信息
@Override
public UserInfoResponse getUserInfo(Integer userId) {
QueryWrapper<UserInfo> queryWrapper=new QueryWrapper<>();
queryWrapper.lambda().eq(UserInfo::getDeleteFlag,Constants.FLAG_NORMAL)
.eq(UserInfo::getId,userId);
UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);
return BeanTranUtils.tran(userInfo);
}
@Override
public UserInfoResponse getAuthorId(Integer blogId) {
//根据博客Id获得博客信息
BlogInfo blogInfo = blogInfoService.getBlogInfo(blogId);
if(blogInfo==null || blogInfo.getUserId()<=0){
throw new BlogException("获取博客信息失败");
}
//根据博客信息获得作者信息
return getUserInfo(blogInfo.getUserId());
}
实现客户端的代码
由于在列表页面和详情页面都需要得到用户信息
所以我们将用户信息的代码提取common.js,直接在blog_list和blog_detail调用getUserInfo就可以
function getUserInfo(url){
$.ajax({
type:"get",
url:url,
success:function(result){
if(result!=null && result.data!=null && result.code==200 ){
var userInfo=result.data;
$(".left .card h3").text(userInfo.userName);
$(".left .card a").attr("href",userInfo.githubUrl);
}
}
});
}
引入common.js
<script src="js/common.js"></script>
进行调用
实现用户退出
只需要前端清除token就行,返回登录界面
注:因为也是好几个界面都可以进行用户退出,所以把这个代码也提取到common.js
function logout(){
localStorage.removeItem("user_token");
localStorage.removeItem("loginUserId");
location.assign("blog_login.html");
}
实现博客的发布
约定好前后端交互的接口
定义请求的实体类
@Data
public class AddBlogRequest {
@NotNull(message = "userId不能为空")
private Integer userId;
@NotBlank(message = "标题不能为空")
private String title;
@NotBlank(message = "内容不能为空")
private String content;
}
实现controller
@RequestMapping("addBlog")
public Boolean addBlog(@RequestBody @Validated AddBlogRequest addBlogRequest){
return blogInfoService.addBlog(addBlogRequest);
}
实现service
Boolean addBlog(AddBlogRequest addBlogRequest);
@Override
public Boolean addBlog(AddBlogRequest addBlogRequest) {
BlogInfo blogInfo=new BlogInfo();
BeanUtils.copyProperties(addBlogRequest,blogInfo);
try {
int result=blogInfoMapper.insert(blogInfo);
if(result==1){
return true;
}
return false;
}catch (Exception e){
log.error("发布错误,请联系管理员");
throw new BlogException("发布错误,请联系管理员");
}
}
editor.md的介绍
是一个开源的⻚⾯markdown编辑器组件
editor.md的使用
实现客户端的代码
$(function () {
var editor = editormd("editor", {
width: "100%",
height: "550px",
path: "blog-editormd/lib/"
});
});
function submit() {
$.ajax({
type:"post",
url:"/blog/addBlog",
contentType:"application/json",
data:JSON.stringify({
userId:localStorage.getItem("loginUserId"),
title:$("#title").val(),
content:$("#content").val()
}),
success: function(result){
if(result!=null && result.code==200 && result.data==true){
location.assign("blog_list.html");
}else{
alert("发布失败");
}
}
});
}
实现删除/编辑博客
编辑博客
约定前后端交互的代码
实现修改博客的实体类
@Data
public class UpdateBlogRequest {
@NotNull(message = "id不能为空")
private Integer id;
private String title;
private String content;
}
实现controller
@RequestMapping("/update")
public Boolean updateBlog(@RequestBody @Validated UpdateBlogRequest updateBlogRequest){
return blogInfoService.updateBlog(updateBlogRequest);
}
实现service
Boolean updateBlog(UpdateBlogRequest updateBlogRequest);
@Override
public Boolean updateBlog(UpdateBlogRequest updateBlogRequest) {
BlogInfo blogInfo=BeanTranUtils.tran(updateBlogRequest);
try {
int result = blogInfoMapper.updateById(blogInfo);
return result==1;
}catch (Exception e){
log.error("编辑失败");
throw new BlogException("内部错误,请联系管理员");
}
}
实现客户端代码
editormd.markdownToHTML("detail", {
markdown: result.data.content,
});
//判断是否显示编辑/删除按钮
let loginUserId = localStorage.getItem("loginUserId");
if(result.data.userId==loginUserId){
//当前作者是登录用户, 显示按钮
let blogId = result.data.id;
let finalHtml = '<button onclick="window.location.href=\'blog_update.html?blogId='+blogId+'\'">编辑</button>';
finalHtml += '<button onclick="deleteBlog('+blogId+')">删除</button>';
console.log(finalHtml);
$(".content .operating").html(finalHtml);
删除博客
约定前后端交互的代码
实现controller
@RequestMapping("/delete")
public Boolean deleteBlog(Integer blogId){
return blogInfoService.deleteBlog(blogId);
}
实现service
删除一篇博客,并不是真正的从数据库中删除,而是将其delete_flag 修改为1
Boolean deleteBlog(Integer id);
@Override
public Boolean deleteBlog(Integer blogId) {
BlogInfo blogInfo=new BlogInfo();
blogInfo.setId(blogId);
blogInfo.setDeleteFlag(Constants.FLAG_DELETE);
try {
int result = blogInfoMapper.updateById(blogInfo);
return result==1;
}catch (Exception e){
log.error("删除失败");
throw new BlogException("内部错误,请联系管理员");
}
}
实现客户端代码
function deleteBlog(blogId) {
$.ajax({
type: "post",
url: "/blog/delete?blogId=" + blogId,
success: function(result){
if(result.code ==200 && result.data==true){
//删除成功
location.href = "blog_list.html";
}else{
alert("删除失败");
}
}
});
alert("删除博客");
}
加密/加盐
目前我们的密码还是明文显示的,为了保护我们的密码,所以要对其加密
密码算法主要分为三类:对称密码算法,非对称密码算法,摘要算法
在博客系统中,我们采用MD5进行加密
采用密码拼接一个随机的字符进行加密,随机的字符我们称之为“盐”
加密的原理
检验的原理
加密工具类
public class SecurityUtils {
public static String encrypt(String inputPassword){
String salt= UUID.randomUUID().toString().replace("-","");
String encryptPassword= DigestUtils.md5DigestAsHex((salt+inputPassword).getBytes(StandardCharsets.UTF_8));
return salt+encryptPassword;
}
public static Boolean verify(String inputPassword,String sqlPassword){
if(!StringUtils.hasLength(inputPassword)){
return false;
}
if(sqlPassword==null || sqlPassword.length()!=64){
return false;
}
String salt=sqlPassword.substring(0,32);
String encryptPassword=DigestUtils.md5DigestAsHex((salt+inputPassword).getBytes(StandardCharsets.UTF_8));
String result=salt+encryptPassword;
return sqlPassword.equals(result);
}
}
注:记得将数据库中的密码改为加密后的密码
自此项目结束,大家可以把这个项目部署到云服务器上
上传服务记得勾选prod,然后刷新Maven
希望能对大家有所帮助!!!!