基于Spring实现博客项目

访问地址:用户登录

代码获取:基于Spring实现博客项目: Spring项目写博客项目 

 一.项目开发

1.项目开发阶段

  1. 需求评审,需求分析
  2. 项目设计(接口设计,DB设计等,比较大的需求,需要设计流程图,用例图,UML, model中的字段)
  3. 开发+自测
  4. 提测(提交测试)
  5. 验收:预发布环境/预生产环境部署测试(开发,测试,产品)
  6. 上线
     

2.前端页面 

1.登录页面: 根据用户名和密码,进行登录
2.博客列表页:显示所有博客列表,以及显示登录用户的个人信息                                                      3.博客详情页:显示当前博客的详细信息,以及作者信息
4.博客插入/编辑页

3.需求分析

1.登录接口(根据用户名和密码,来判断是否登录成功)                                                                       2.根据用户ID,获取用户相关信息
3.获取所有的博客列表
4.根据博客ID,获取博客的详情信息(作者ID)                                                                                     5.根据博客ID,更新博客内容
6.插入博客
7.删除博客

4.数据库设计

        --建表sql
        create 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-java4
        5");
        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=false
    username: root
    password: woaini520
    driver-class-name: com.mysql.cj.jdbc.Driver

#  mybatis xml 配置路径
mybatis:
  mapper-locations: classpath:mapper/**Mapper.xml
  configuration: # 配置打印 MyBatis 执行的 SQL
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true  #驼峰转换

application-prod.yml

server:
  port: 8080


# 数据库连接配置
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/java_blog_spring?characterEncoding=utf8&useSSL=false
    username: root
    password: woaini520
    driver-class-name: com.mysql.cj.jdbc.Driver

#  mybatis xml 配置路径
mybatis:
  mapper-locations: classpath:mapper/**Mapper.xml
  configuration:
    map-underscore-to-camel-case: true

5.测试类

userMapper

@SpringBootTest
@Slf4j
class UserMapperTest {
    @Autowired
    UserMapper userMapper;

    @Test
    void selectById() {
        User user = userMapper.selectById(1);
        log.info(user.toString());
    }

    @Test
    void selectByName() {
        User user = userMapper.selectByName("zhangsan");
        log.info(user.toString());
    }
}

 

blogMapper

@SpringBootTest
@Slf4j
class BlogMapperTest {
    @Autowired
    BlogMapper blogMapper;

    @Test
    void selectAll() {
        List<Blog> blogs = blogMapper.selectAll();
        log.info(blogs.toString());

    }

    @Test
    void selectById() {
        Blog blog = blogMapper.selectById(1);
        log.info(blog.toString());
    }

    @Test
    void updateBlog() {
        Blog blog = new Blog();
        blog.setId(1);
        blog.setTitle("测试的第一篇博客");
        blog.setTitle("测试的第一篇博客的正文内容");
        blogMapper.updateBlog(blog);
    }

    @Test
    void 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 {
    @ExceptionHandler
    public Result error(Exception e) {

        return Result.fail(-1, e.getMessage());

    }
}

3.统一返回格式处理 

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @SneakyThrows
    @Override
    public 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 {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private 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 {
    @Autowired
    private 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 {
    @Autowired
    BlogService 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 {
    @Autowired
    UserService 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 {
    @Override
    public 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 {
    @Autowired
    private LoginInterceptor loginInterceptor;

    private final List<String> excludes = Arrays.asList(
            "/**/*.html",
            "/blog-editormd/**",
            "/css/**",
            "/js/**",
            "/pic/**",
            "/user/login"
    );

    @Override
    public 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 + '">查看全文&gt;&gt;</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 {

    @Test
    void 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生成随机的字符串.

    @Test
    void 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含有特殊字符,因此我们可以通过以下的方式将"-"去掉

    @Test
    void 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

添加安全组

 否则无法正常访问

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/94616.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网易新财报:游戏稳、有道进、云音乐正爬坡

今年以来&#xff0c;AI大模型的火热程度屡屡攀升&#xff0c;越来越多的企业都加入到了AI大模型的赛场中&#xff0c;纷纷下场布局。而在众多参与者中&#xff0c;互联网企业的身影更是频频浮现&#xff0c;比如&#xff0c;百度、阿里巴巴、腾讯等等。值得一提的是&#xff0…

同态比较算法

参考文献&#xff1a; [PS73] Paterson M S, Stockmeyer L J. On the number of nonscalar multiplications necessary to evaluate polynomials[J]. SIAM Journal on Computing, 1973, 2(1): 60-66.[IZ21] Iliashenko I, Zucca V. Faster homomorphic comparison operations …

《Flink学习笔记》——第八章 状态管理

8.1 Flink中的状态 8.1.1 概述 在Flink中&#xff0c;算子任务可以分为无状态和有状态两种情况。 **无状态的算子&#xff1a;**每个事件不依赖其它数据&#xff0c;自己处理完就输出&#xff0c;也不需要依赖中间结果。例如&#xff1a;打印操作&#xff0c;每个数据只需要…

(AS笔记)上传aar包到Maven中央仓库

目录 一、SonaType账户注册与登录 &#xff08;1&#xff09;注册 &#xff08;2&#xff09;登录 二、创建工单 &#xff08;1&#xff09;Github子域名验证 &#xff08;2&#xff09;自定义域名验证 三、登录Nexus Repository Manager 四、GPG签名生成和发布 五、Andr…

IEC 60068 环境测试介绍及其标准下载

IEC 60068 环境测试介绍及其标准下载 IEC 60068 标准由国际电工委员会 (IEC) 发布&#xff0c;是用于电工产品环境测试的国际标准。 IEC 60068 系列包含有关标准、环境测试程序和测试严重性的基本信息。 IEC 60068 环境测试 制定这一系列标准是为了在特定产品类型&#xff08…

C语言(第三十一天)

6. 调试举例1 求1!2!3!4!...10!的和&#xff0c;请看下面的代码&#xff1a; #include <stdio.h> //写一个代码求n的阶乘 int main() {int n 0;scanf("%d", &n);int i 1;int ret 1;for(i1; i<n; i){ret * i;}printf("%d\n", ret);return …

线性代数(五) 线性空间

前言 《线性代数(三) 线性方程组&向量空间》我通过解线性方程组的方式去理解线性空间。此章从另一个角度去理解 空间是什么 大家较熟悉的&#xff1a;平面直角坐标系是最常见的二维空间 空间由无穷多个坐标点组成 每个坐标点就是一个向量 反过来&#xff0c;也可说&…

【附安装包】CAD2024(建筑版)安装教程

软件下载 软件&#xff1a;CAD建筑版本&#xff1a;2023语言&#xff1a;简体中文大小&#xff1a;4.52G安装环境&#xff1a;Win11/Win10硬件要求&#xff1a;CPU2.5GHz 内存8G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a;https://pan.baidu.com/s/1cHe…

万级数据优化EasyExcel+mybatis流式查询导出封装

文章目录 前言.万级数据优化一. 直接上流式查询封装工具代码二. 传统分页导出查询三. 流式查询概念游标查询 前言.万级数据优化 我们不妨先给大家讲一个概念&#xff0c;利用此概念我们正好给大家介绍一个数据库优化的小技巧&#xff1a; 需求如下&#xff1a;将一个地市表的数…

CSS中如何实现文字阴影效果(text-shadow)?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 实现思路⭐ 示例⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前…

视频汇聚/视频云存储/视频监控管理平台EasyCVR安全检查的相关问题及解决方法

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

解读GIS软件:从ArcGIS到山海鲸可视化的全方位介绍

在现代社会&#xff0c;地理信息系统&#xff08;GIS&#xff09;的应用已经渗透到了各个领域&#xff0c;为我们提供了丰富的地理数据分析和可视化工具。下面介绍几款常见的GIS工具软件&#xff0c;一起来了解它们的特点和优势。 1. ArcGIS: ArcGIS由Esri公司开发&#xff0c;…

php环境搭建步骤(与资源配套使用版)

1.将phpEnv.zip下载到D盘下 2.解压到当前文件夹 3.找到Apache24下的bin目录&#xff0c;执行cmd操作&#xff0c;回车。 4.在cmd中执行代码 Httpd -k install -n “Apache24” 4.使用winR键打开运行&#xff0c;输入services.msc &#xff0c;回车&#xff0c;进入服务 …

ipad有必要用手写笔吗?开学季实惠的电容笔推荐

iPad平板的机型经过了一次又一次的升级&#xff0c;增加了更多的功能&#xff0c;如今已有了与笔记本电脑匹敌的能力。而到了如今&#xff0c;科技的发展&#xff0c;iPad也从一个娱乐工具&#xff0c;变成了一个集学习、画画、办公于一体的强大工具。为了提高生产效率&#xf…

PHP教学资源管理系统Dreamweaver开发mysql数据库web结构php编程计算机网页

一、源码特点 PHP 教学资源管理系统是一套完善的web设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 源码 https://download.csdn.net/download/qq_41221322/88260480 论文 https://downl…

入海排污口水质自动监测系统,助力把好入河入海“闸门”

随着经济社会的不断发展&#xff0c;污水的排放强度不断加大&#xff0c;大量的污水排入河流、湖泊和海洋中&#xff0c;造成了水体污染&#xff0c;严重影响着我国的用水安全、公众健康、经济发展与社会稳定。入河入海排污口是污染物进入河流和海洋的最后关口&#xff0c;也是…

im6ull-uboot(2021.07)移植(一)

文章目录 声明1 获取源码1.1 从u-boot官网获取1.2 从芯片厂商获取1.3 从开发板厂商获取 2 修改顶层Makefile3 xxx_defconfig配置文件3.1 拷贝生成自己的配置文件3.2 修改defconfig文件3.2.1 查看defconfig文件3.2.2 修改defconfig文件 3.3 添加其他配置文件3.3.1 添加配置头文件…

计算机视觉与人工智能在医美人脸皮肤诊断方面的应用

一、人脸皮肤诊断方法 近年来&#xff0c;随着计算机技术和人工智能的不断发展&#xff0c;中医领域开始逐渐探索利用这些先进技术来辅助面诊和诊断。在皮肤望诊方面&#xff0c;也出现了一些现代研究&#xff0c;尝试通过图像分析技术和人工智能算法来客观化地获取皮肤相关的…

微软 Visual Studio 现已内置 Markdown 编辑器,可直接修改预览 .md 文件

Visual Studio Code V1.66.0 中文版 大小&#xff1a;75.30 MB类别&#xff1a;文字处理 本地下载 Markdown 是一种轻量级标记语言&#xff0c;当开发者想要格式化代码但又不想牺牲易读性时&#xff0c;Markdown 是一个很好的解决方案&#xff0c;比如 GitHub 就使用 Markdo…

7个用于机器学习和数据科学的基本 Python 库

推荐&#xff1a;使用 NSDT场景编辑器 助你快速搭建3D应用场景 这篇文章针对的是刚开始使用Python进行AI的人&#xff0c;以及那些有经验的人&#xff0c;但对下一步要学习什么有疑问的人。我们将不时花点时间向初学者介绍基本术语和概念。如果您已经熟悉它们&#xff0c;我们鼓…