编程(46)----------个人项目

本篇主要用于整理个人项目的内容

1.个人项目框架

该项目所依赖的是SSM(Spring+SpringMVC+MyBatis)框架. 与Spring相比, SSM是在此基础的升级版, 集成了上述三者的功能. 就好比Spring是教人如何开车, 而SSM是在会开车的基础上, 又增加了如自动驾驶的功能, 具体三者是什么, 也需要简单说明一下.

Spring可以理解为, 用于存放各种工具的LoC(lnversion of Control)容器. LoC即控制反转. 其核心思想是对对象生命周期的控制反转. 传统代码编写的时候, 要在A类中调用B类, 就需要在A类中将B类给new出来. 换句话说, 调用类的生命周期是完全由程序员所决定的, 而控制反转就是将类的生命周期由程序员掌控变为由框架Spring进行掌控.

 除此以外, 提到LoC就不得不提到DI(dependency injection), 即依赖注入. 广义上来讲, 可以将依赖注入与控制反转可以等同看待. 就好比手机内存, 广义上所说的手机内存256G实际上是指的其存储量, 并非特指其运行内存. 从狭义来讲, 控制反转与依赖注入是不同的. 控制反转是一种将对象生命周期托管到框架的一种思想. 而依赖注入就是其具体的实现方式.

最后一个问题, 为什么要使用控制反转. 传统的使用对象new一个对象有什么不好的地方? 答案在于耦合性. 传统new对象在连续调用代码中耦合性是极高的, 牵一发而动全身. 当某一处的对象发生改变会产生连锁反应导致整个调用链都会出现问题. 而使用控制反转后解耦合能避免这样的问题.

接着再来说说什么是SpringMVC. 首先得了解一下什么是MVC. 其全称是Model View Controller. Model用于处理数据逻辑部分, 例如与数据库进行沟通, View则是用于展示给用户的内容, Controller这是需要得到用户发出的请求, 去访问数据库得到响应, 然后发给View处理最后展现给用户. 早期的MVC的运作大概是这个样子的:

                                                                             

因此, MVC实际上是指的一种软件的构架模式. 以这种方式实现软件程序与用户的交互. 而SpringMVC与MVC的关系就好比上文所提到的LoC和DI的关系. MVC是一种思想一种模式, SpringMVC是其具体的实现方式.

最后是Mybatis. Mybatis可以理解为一种用于数据库映射的框架. 在有其支持的前提下可以通过代码直接对数据库进行操作, 使得对数据库的操作更为简单, 类似于这样:

<?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.example.demo.mapper.CommentMapper">
    <insert id="comment">
        insert into comment(title, content) values (#{title}, #{content})
    </insert>

    <select id="getListByPage" resultType="com.example.demo.entity.Comment">
        select * from comment  where state = 1 limit #{psize} offset #{offsize}
    </select>

    <select id="getCount" resultType="Integer">
        select count(*) from comment
    </select>

</mapper>

2.加密算法

在该项目中需要进行注册登录操作. 但是注册操作中不可能直接将明面上的密码注入到数据库中, 这样是不太安全的. 因此需要加密操作. 常见的加密操作如MD5加密, 并不适用于此. 因为这类加密操作是固定的. 每个字符都对应了一个确定的加密密码. 因此可以以加密后的密码进行一一对照从而破解密码.

所以在该项目中并未使用MD5进行加密, 而是使用加盐算法. 加密后的密码由盐值加上加密后的密码构成. 由于在进行加密的时候盐值是随机的, 因此将其与密码结合所生成的加密后的密码也是随机的, 破解难度会比较大. 而要达成解密, 就只能在输入时获取盐值再次加密一次.

    /**
     * 1.加盐并生成密码
     *
     * @param password 明文密码
     * @return 保存到数据库中的密码
     */
    public static String encrypt(String password){
        //a.产生盐值(32位)
        String salt = UUID.randomUUID().toString().replace("-", "");
        //2.生成加盐之后的密码
        String saltPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());
        //3.生成最终密码(保存到数据库中的密码)[约定格式:32位盐值+$+32位加盐之后的密码]
        String finalPassword = salt + "$" + saltPassword;
        return finalPassword;
    }

    /**
     * 2.生成加盐的密码(方法1的重载)
     * @param password 明文
     * @param salt 固定的研制
     * @return 最终密码
     */
    public static String encrypt(String password, String salt){
        //1.生成一个加盐之后的密码
        String saltPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());
        //2.生成最终密码[约定格式:32位盐值+$+32位加盐之后的密码]
        String finalPassword = salt + "$" + saltPassword;
        return finalPassword;
    }

    /**
     * 3.验证密码
     * @param inputPassword 用户输入的明文密码
     * @param finalPassword 数据库保存的最终密码
     * @return
     */
    public static boolean check(String inputPassword, String finalPassword){
        if (StringUtils.hasLength(inputPassword) && StringUtils.hasLength(finalPassword) &&
        finalPassword.length() == 65){
            //1.得到盐值
            String salt = finalPassword.split("\\$")[0];
            //2.使用之前的步骤将明文密码和已经得到的盐值进行加密, 生成最终密码
            String confirmPassword = PasswordUtils.encrypt(inputPassword, salt);
            //3.对比两个最终密码是否相同
            return confirmPassword.equals(finalPassword);
        }
        return false;
    }

3.支持redis分布式

一个大型的项目要实现数以万计的人进行访问, 那么这个项目必然不可能只存在于某一个服务器当中, 而必须实现多个服务器分布. 但是这时存在一个问题. 假设某个项目客户第一次登录访问时是在服务器A中, 登录后服务器A创建了session以便于进行项目其他操作. 但是由于该项目是分布于多个服务器当中的, 那完全有可能客户在进行二次访问时恰好访问的是服务器B. 那么由于session只存在于服务器A中, 故需要再次执行登录操作.

很显然这是不科学的. 如果服务器够多, 可能会出现客户每次进行操作访问都需要进行登录的情况. 因此, 才需要进行redis分布式. 其主要作用是将session从众多服务器中剥离出来, 单独存储session, 这样无论访问哪个服务器, 服务器只需访问唯一的redis中存储的session即可, 得以避免多次反复登录的情况

                                                                       

4. 统一返回格式

一个正常运行的项目, 是一定会涉及到前后端交互的. 在生产环境中, 作为后端而言, 若不进行统一的格式返回, 对于前端而言处理数据是极其痛苦的. 因此必须要做好统一的格式返回. 

但又存在一个问题, 有时候程序员可能会忘记或者压根不知道要统一返回格式. 因此统一格式的返回必须是强制性的, 即没有在业务代码中进行统一, 也必须在返回前进行拦截检查, 统一后再返回于后端

package com.example.demo.config;

import com.example.demo.common.AjaxResult;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * 实现统一数据返回的保底类
 * 最后进行检测
 */
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {

    @Autowired
    private ObjectMapper objectMapper;

    /**
     *开关, 为true才会调用下一个方法
     */
    @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 AjaxResult) return body;
        if(body instanceof String){
            return objectMapper.writeValueAsString(AjaxResult.success(body));
        }
        return AjaxResult.success(body);
    }
}

5.登录拦截器

正如前面所说, 项目是需要登录才能进行操作的. 一个项目的有些内容若不进行登录, 即不能进行相关的访问操作. 所以每次进行访问操作之前都需要对请求进行拦截, 以检查是否登录. 若已登录再进行请求的发送访问. 当然并非所有的项目内容都需要登录才能操作. 因此也要放开部分项目. 

在该项目中就实现了一个简单的拦截器, 并排除部分非拦截内容, 如前端的渲染.

package com.example.demo.config;

import com.example.demo.common.AppVariable;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class LoginInterceptor implements HandlerInterceptor {
    /**
     * true 用户已登录
     * false 用户未登录
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute(AppVariable.USER_SESSION_KEY) != null){
            //用户已登录
            return true;
        }
        //用户未登录
        response.sendRedirect("/login.html");
        return false;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值