零、 login.html / SessionInterceptor

<html>
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="assets/global/plugins/bootstrap/css/bootstrap.min.css" type="text/css"/>
    <link rel="stylesheet" href="assets/global/css/components.css" type="text/css"/>
    <link rel="stylesheet" href="assets/admin/pages/css/login.css" type="text/css"/>
    <script rel="stylesheet" src="assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
    <link rel="stylesheet" href="selectMeiHua.css">
</head>
<body class="login" background="edge_bj_1.jpg">

<div class="content">
    <h3 class="form-title">用户登录</h3>
    <div class="form-group">
        <label class="control-label">手机号</label>
        <div>
            <input type="text" class="form-control" placeholder="手机号" name="telphone" id="telphone">
        </div>
    </div>
    <div class="form-group">
        <label class="control-label">密码</label>
        <div>
            <input type="password" class="form-control" placeholder="密码" name="password" id="password">
        </div>
    </div>
    <div>
        <button class="btn green" id="login" type="submit">
            登录
        </button>
        <button class="btn green" id="toRegister">
            注册
        </button>
    </div>
</div>

</body>
<script>

    jQuery(document).ready(function () {

        //跳转到注册页面
        $("#toRegister").on("click", function () {

            window.location.href="getotp.html";
        })

        //绑定otp的click事件用于向后端发送手机验证码的请求
        $("#login").on("click", function () {

            var tel = $("#telphone").val();
            var password = $("#password").val();
            if (tel == null || tel == '') {
                alert("用户手机号不能为空");
                return false;
            }
            if (password == null || password == '') {
                alert("用户密码不能为空");
                return false;
            }
            $.ajax({
                type: "POST",
                contentType: "application/x-www-form-urlencoded",
                url: "http://localhost:8080/user/login",
                data: {
                    "telphone": $("#telphone").val(),
                    "encrptPassword": $("#password").val(),
                },
                dataType: "json",
                xhrFields: {
                    withCredentials: true
                },
                crossDomain: true,
                success: function (result) {

                    if (result.status == "success") {
                        alert(result.data)
                    } else {
                        alert("请求失败 原因为:" + result.data.errMsg)
                    }
                },
                error: function (data) {
                    alert("请求失败 原因为:" + data.responseText)
                }
            });
        })
    })

</script>
</html>
 1 package com.miaoshaProject.login_config;
 2 
 3 import com.miaoshaProject.dataobject.UserDO;
 4 import com.miaoshaProject.service.model.UserModel;
 5 import org.springframework.stereotype.Component;
 6 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 7 
 8 import javax.servlet.http.HttpServletRequest;
 9 import javax.servlet.http.HttpServletResponse;
10 import java.io.PrintWriter;
11 
12 /**
13  * @Description 登录超时拦截器
14  * @Author rongtao
15  * @Data 2019/4/24 13:37
16  * https://www.codeleading.com/article/4517856247/
17  */
18 @Component
19 public class SessionInterceptor extends HandlerInterceptorAdapter {
20     //拦截action
21     @Override
22     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
23             throws Exception {
24         UserModel user = (UserModel) request.getSession().getAttribute("LOGIN_USER");
25         System.out.println(user);
26         //session中User过期
27         if(user == null){
28             String uri = request.getRequestURI();
29             System.out.println(uri);
30             //ajax请求响应头会有,x-requested-with
31             if (request.getHeader("x-requested-with") != null && request.getHeader("x-requested-with")
32                     .equalsIgnoreCase("XMLHttpRequest")) {
33                 //在响应头设置session状态
34                 response.setHeader("sessionstatus", "timeout");
35                 response.setHeader("url", uri.substring(0, uri.indexOf("/", 1)));
36             } else {
37                 PrintWriter out = response.getWriter();
38                 StringBuilder sb = new StringBuilder();
39                 sb.append("<script type=\"text/javascript\" charset=\"UTF-8\">");
40                 sb.append("alert(\"登录超时,请重新登录\");");
41                 sb.append("window.top.location.href=\"");
42                 sb.append("/login.html");
43                 sb.append("\";</script>");
44                 out.print(sb.toString());
45                 out.close();
46             }
47             //返回false不再调用其他的拦截器和处理器
48             return false;
49         }
50         return true;
51     }
52 }

 

一、项目层级

 

 二、ValidatorImpl

 1 package com.miaoshaProject.validator;
 2 
 3 import org.springframework.beans.factory.InitializingBean;
 4 import org.springframework.stereotype.Component;
 5 
 6 import javax.validation.ConstraintViolation;
 7 import javax.validation.Validation;
 8 import javax.validation.Validator;
 9 import java.util.Set;
10 
11 /**
12  * @Author wangshuo
13  * @Date 2022/4/16, 14:14
14  * Please add a comment
15  */
16 @Component//声明bean
17 public class ValidatorImpl implements InitializingBean {
18 
19     private Validator validator;
20 
21     //实现校验方法并返回校验结果
22     public ValidationResult validator(Object bean){
23 
24         final ValidationResult result = new ValidationResult();
25         final Set<ConstraintViolation<Object>> set = validator.validate(bean);
26         if (set.size() > 0){
27             result.setHasErrors(true);
28             //Lambda表达式
29             set.forEach(ConstraintViolation->{
30                 String errMsg = ConstraintViolation.getMessage();
31                 String propertyName = ConstraintViolation.getPropertyPath().toString();
32                 result.getErrorMap().put(propertyName,errMsg);
33             });
34         }
35         return result;
36     }
37 
38     @Override
39     public void afterPropertiesSet() throws Exception {
40         //将hibernate validator通过工厂的初始化方式使其实例化
41         this.validator = Validation.buildDefaultValidatorFactory().getValidator();
42     }
43 }

三、ValidationResult

 1 package com.miaoshaProject.validator;
 2 
 3 import org.apache.commons.lang3.StringUtils;
 4 
 5 import java.util.HashMap;
 6 import java.util.Map;
 7 
 8 /**
 9  * @Author wangshuo
10  * @Date 2022/4/16, 14:06
11  * Please add a comment
12  */
13 public class ValidationResult {
14 
15     //校验结果
16     private boolean hasErrors = false;
17 
18     //存放错误信息的map
19     private Map<String,String> errorMap = new HashMap<>();
20 
21     //实现通用的通过格式化字符串获取错误结果的方法
22     public String getErrMsg(){
23 
24         //map --》 array --》 String
25         String join = StringUtils.join(errorMap.values().toArray(), ",");
26         return join;
27     }
28 
29     public boolean isHasErrors() {
30         return hasErrors;
31     }
32 
33     public void setHasErrors(boolean hasErrors) {
34         this.hasErrors = hasErrors;
35     }
36 
37     public Map<String, String> getErrorMap() {
38         return errorMap;
39     }
40 
41     public void setErrorMap(Map<String, String> errorMap) {
42         this.errorMap = errorMap;
43     }
44 }

四、UserModel

 1 package com.miaoshaProject.service.model;
 2 
 3 import javax.validation.constraints.Max;
 4 import javax.validation.constraints.Min;
 5 import javax.validation.constraints.NotBlank;
 6 import javax.validation.constraints.NotNull;
 7 import java.io.Serializable;
 8 
 9 /**
10  * @Author wangshuo
11  * @Date 2022/4/12, 19:46
12  * 要实现 Serializable接口,不然没办法放到session中
13  */
14 public class UserModel implements Serializable {
15 
16     private Integer id;
17     @NotBlank(message = "用户名不能为空")
18     private String name;
19     @NotNull(message = "性别填写有误")
20     private Integer gender;
21     @NotNull(message = "年龄填写有误")
22     @Min(value = 0, message = "年龄必须大于零")
23     @Max(value = 150, message = "年龄不能大于150")
24     private Integer age;
25     @NotBlank(message = "手机号不能为空")
26     private String telpphone;
27     private String registerMode;
28     private String thiredPartyId;
29     @NotBlank(message = "密码不能为空")
30     private String encrptPassword;
31 
32     public Integer getId() {
33         return id;
34     }
35 
36     public void setId(Integer id) {
37         this.id = id;
38     }
39 
40     public String getName() {
41         return name;
42     }
43 
44     public void setName(String name) {
45         this.name = name;
46     }
47 
48     public Integer getGender() {
49         return gender;
50     }
51 
52     public void setGender(Integer gender) {
53         this.gender = gender;
54     }
55 
56     public Integer getAge() {
57         return age;
58     }
59 
60     public void setAge(Integer age) {
61         this.age = age;
62     }
63 
64     public String getTelpphone() {
65         return telpphone;
66     }
67 
68     public void setTelpphone(String telpphone) {
69         this.telpphone = telpphone;
70     }
71 
72     public String getRegisterMode() {
73         return registerMode;
74     }
75 
76     public void setRegisterMode(String registerMode) {
77         this.registerMode = registerMode;
78     }
79 
80     public String getThiredPartyId() {
81         return thiredPartyId;
82     }
83 
84     public void setThiredPartyId(String thiredPartyId) {
85         this.thiredPartyId = thiredPartyId;
86     }
87 
88     public String getEncrptPassword() {
89         return encrptPassword;
90     }
91 
92     public void setEncrptPassword(String encrptPassword) {
93         this.encrptPassword = encrptPassword;
94     }
95 }

五、UserController

 1 //用户注册接口
 2     @RequestMapping(value = "/login", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
 3     @ResponseBody
 4     public CommonReturnType userRegister(@RequestParam(name = "telphone") String telphone,
 5                                          @RequestParam(name = "encrptPassword") String encrptPassword) throws BusinessException, NoSuchAlgorithmException {
 6 
 7         //入参校验
 8         if (StringUtils.isEmpty(telphone) || StringUtils.isEmpty(encrptPassword)){
 9             throw new BusinessException(EnumBusinessError.PARAMETER_VALIDATION_ERROR);
10         }
11         //用户登录服务
12         UserModel userModel = uesrService.validateLogin(telphone, this.encodeByMD5(encrptPassword));
13         //服务层没throw Exception,用户登录成功,将凭证加入session内
14         this.httpServletRequest.getSession().setAttribute("LOGIN_USER",userModel);
15         return CommonReturnType.create("登录成功");
16     }

六、UserServiceImpl

 1 @Override
 2     @Transactional//保证事务唯一性
 3     public void register(UserModel userModel) throws BusinessException {
 4 
 5         /*if (userModel == null)
 6             throw new BusinessException(EnumBusinessError.PARAMETER_VALIDATION_ERROR);
 7         if (StringUtils.isEmpty(userModel.getName()) || userModel.getGender() == null ||
 8                 StringUtils.isEmpty(userModel.getTelpphone()) || userModel.getAge() == null ||
 9                 StringUtils.isEmpty(userModel.getRegisterMode())) {
10 
11             throw new BusinessException(EnumBusinessError.PARAMETER_VALIDATION_ERROR);
12         }*/
13         //更新校验规则
14         ValidationResult result = this.validator.validator(userModel);
15         if (result.isHasErrors())
16             throw new BusinessException(EnumBusinessError.PARAMETER_VALIDATION_ERROR,result.getErrMsg());
17         UserDO userDO = convertFromModelObject(userModel);
18         //使用selective
19         //处理用户重复注册异常
20         try {
21             userDOMapper.insertSelective(userDO);
22         } catch (DuplicateKeyException e) {
23             throw new BusinessException(EnumBusinessError.REGISTER_REPEAT);
24         }
25 
26         //把刚刚新增成功的userId赋值给UserPasswordDO用于密码表的新增
27         userModel.setId(userDO.getId());
28         UserPasswordDO userPasswordDO = convertPasswordFromModelObject(userModel);
29         userPasswordDOMapper.insertSelective(userPasswordDO);
30     }
31 
32     @Override
33     public UserModel validateLogin(String telphone, String encrptPassword) throws BusinessException {
34 
35         UserDO userDO = userDOMapper.selectByTelphone(telphone);
36         if (userDO == null)
37             throw new BusinessException(EnumBusinessError.LOGIN_ERROR);
38         UserPasswordDO userPasswordDO = userPasswordDOMapper.selectByUserId(userDO.getId());
39         UserModel userModel = convertFromDataObject(userDO, userPasswordDO);
40         //比对用户信息内加密的密码是否与传输来的密码相匹配
41         if (!StringUtils.equals(encrptPassword, userModel.getEncrptPassword())) {
42 
43             throw new BusinessException(EnumBusinessError.LOGIN_ERROR);
44         }
45         //登录成功,返回model
46         return userModel;
47     }

七、UserDOMapper

<select id="selectByTelphone" resultMap="BaseResultMap">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Thu Apr 14 10:32:50 CST 2022.
    -->
    select
    <include refid="Base_Column_List" />
    from user_info
    where telpphone = #{telphone,jdbcType=VARCHAR}
  </select>

 八、EnumBusinessError

 1 package com.miaoshaProject.error;
 2 
 3 /**
 4  * @Author wangshuo
 5  * @Date 2022/4/14, 8:43
 6  * 自定义error
 7  */
 8 public enum EnumBusinessError implements CommonError{
 9     //10001 参数不合法
10     PARAMETER_VALIDATION_ERROR(10001,"参数不合法"),
11     //20000未知错误
12     UNKNOWN_ERROR(20000,"未知错误"),
13     //以30000开头的错误码代表用户信息错误
14     USER_NOT_EXISTS(30001,"用户不存在"),
15     REGISTER_OTP_ERROR(30002,"验证码错误"),
16     REGISTER_REPEAT(30003,"该手机号已注册账户,请勿重复注册"),
17     LOGIN_ERROR(30004,"用户手机号或密码不正确")
18     ;
19 
20     private EnumBusinessError(Integer code,String msg){
21 
22         this.errorCode = code;
23         this.errorMsg = msg;
24     }
25 
26     private int errorCode;
27     private String errorMsg;
28 
29     @Override
30     public int getErrorCode() {
31         return this.errorCode;
32     }
33 
34     @Override
35     public String getErrorMsg() {
36         return this.errorMsg;
37     }
38 
39     //定制化的方法改动错误信息
40     @Override
41     public CommonError setErrorMsg(String msg) {
42         this.errorMsg = msg;
43         return this;
44     }
45 }