项目:博客系统——基于SSM框架&Mybatis-plus

基于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 + '">查看全文&gt;&gt;</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

希望能对大家有所帮助!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值