注册

持久层

通过mybatis操作数据库

sql语句

1.用户注册,相当于插入

1
insert into t_user(username,password,phone_num)values()

2.先查询用户名是否存在

1
select * from t_user where username=?
设计接口和抽象方法

定义Mapper接口

创建一个Mapper包

创建UserMapper接口,定义sql的抽象方法

1
2
3
4
5
6
7
8
9
10
11
12
package com.cy.store.mapper;


import com.cy.store.pojo.User;
import org.apache.ibatis.annotations.Mapper;


public interface UserMapper {
Integer insert(User user);
User findByUsername(String username);
}

1
@MapperScan("com.cy.store.mapper")
编写映射

1.定义xml映射文件,与接口关联,所有映射文件在resources,创建一个mapper文件夹,存放Mapper的映射文件.

2.创建接口对应的映射文件

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace:指定接口文件路径,完整-->
<mapper namespace="com.cy.store.mapper.UserMapper">

</mapper>

3.配置sql语句,借助标签完成,insert/update/delete/select

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace:指定接口文件路径,完整-->
<mapper namespace="com.cy.store.mapper.UserMapper">
<!-- id:给映射一个唯一的id值
type:实体类-->
<resultMap id="userEntityMap" type="com.cy.store.pojo.User">

</resultMap>
<!-- id:表示接口中方法名称
userGeneratedKeys:开启递增
keyProperty:以某字段为主键-->
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO t_user(username,password,salt,email) VALUES
{
#{username},#{password},#{salt},#{email}
}
</insert>
<!--selcect 查询的结果是一个对象,多个对象-->
<!--resultType:表和类一致-->
<!--resultMap:不一致-->
<select id="findByUsername" resultType="UserEntityMap">
SELECT * FROM t_user WHERE username = #{username}
</select>
</mapper>

4.将mapper注册到yml里

5.单元测试:每个独立层编写完成编写单元测试方法,来测试功能.在test下创建mapper包

业务层

规划异常

1.RuntimeException,作为异常的子类,再定义个异常类来继承.

业务层异常基类,ServiceException异常,继承RuntimeException异常.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.cy.store.service.ex;

/*
作为业务层异常的基类:thows new ServiceException("业务层产生的异常")
*/
public class ServiceException extends RuntimeException{
public ServiceException() {
super();
}

public ServiceException(String message) {
super(message);
}

public ServiceException(String message, Throwable cause) {
super(message, cause);
}

public ServiceException(Throwable cause) {
super(cause);
}

protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}


根据业务层不同的功能来定义异常类型,统一去继承

2.用户在进行时产生用户名被占用的错误,抛出异常:UsernameDuplicstedException异常.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.cy.store.service.ex;

/*
用户名被占用
*/
public class UsernameDuplicstedException extends ServiceException{
//alt+insert --overide methods

public UsernameDuplicstedException() {
super();
}

public UsernameDuplicstedException(String message) {
super(message);
}

public UsernameDuplicstedException(String message, Throwable cause) {
super(message, cause);
}

public UsernameDuplicstedException(Throwable cause) {
super(cause);
}

protected UsernameDuplicstedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}


3.正在执行数据操作时数据库宕机.处于正在插入时异常:InsertException

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.cy.store.service.ex;

/*
数据插入时产生的异常
*/
public class InsertException extends ServiceException{
public InsertException() {
super();
}

public InsertException(String message) {
super(message);
}

public InsertException(String message, Throwable cause) {
super(message, cause);
}

public InsertException(Throwable cause) {
super(cause);
}

protected InsertException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}


设计接口和抽象方法

在service包下创建UserService接口.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.cy.store.service;

import com.cy.store.pojo.User;

/*
用户模块业务层接口
*/
public interface UserService {
/*
用户注册方法
*/
void reg(User user);

}

2.创建一个实现类UserServiceImpl,需要实现这个接口,并抽象方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.cy.store.service.impl;

import com.cy.store.mapper.UserMapper;
import com.cy.store.pojo.User;
import com.cy.store.service.UserService;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.UsernameDuplicstedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.util.UUID;

@Service//将当前类交给spring管理,自动创建对象以及对象的维护
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public void reg(User user) {
//通过user获取username
String username = user.getUsername();
//调用findByUsername(username)判断是否被注册
User result = userMapper.findByUsername(username);
//判断结果集是否不为null抛出异常
if(result != null){
//抛出异常
throw new UsernameDuplicstedException("用户名被占用");

}
//密码加密:md5
//串+password+串 --md5算法加密,连续三次
//盐值+password+盐值 --盐值是随机串
String oldPassword = user.getPassword();
String salt = UUID.randomUUID().toString().toUpperCase();
//将密码和盐值加密处理
String md5Password = getMD5Password(oldPassword,salt);
//忽略原密码强度提升安全性
user.setPassword(md5Password);
user.setSalt(salt);
//执行注册功能(rows==1)
Integer rows = userMapper.insert(user);
if(rows != 1){
throw new InsertException("用户注册过程中产生未知异常");

}
}

/*
定义一个md5算法加密
*/
private String getMD5Password(String password,String salt){
//md5加密
for(int i = 0;i < 3;i++){
password = DigestUtils.md5DigestAsHex((salt+password+salt).getBytes()).toUpperCase();
}
return password;
}
}


3.在`单元测试包下常见UserServiceTests类,加入单元测试

控制层

创建响应

状态码,状态描述信息,数据封装在一个类里,并作为返回值返回给前端

1
2
3
4
5
6
7
8
9
10
11
12
/*
json格式响应
*/
public class JasonResult<E> implements Serializable {
//响应状态码
private Integer state;
//描述信息
private String message;
//数据
private E data;
}

设计请求
1
2
3
4
5
请求路径: users/reg
请求参数: User user
请求类型: post
响应结果: JavaResult<void>

处理请求

1.创建控制层对应类UserController类,依赖于业务层接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.cy.store.controller;

import com.cy.store.pojo.User;
import com.cy.store.service.UserService;
import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.UsernameDuplicstedException;
import com.cy.store.util.JasonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController //controller + response
@RequestMapping("users")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("reg")
public JasonResult<Void> reg(User user){
//创建响应对象
JasonResult<Void> result =new JasonResult<>();
try {
userService.reg(user);
result.setState(200);
result.setMessage("注册成功");
} catch (UsernameDuplicstedException e) {
result.setState(4000);
result.setMessage("用户名被占用");
} catch (InsertException e) {
result.setState(5000);
result.setMessage("注册时产生未知异常");
}
return result;
}
}


控制层优化设计

在控制层抽象一个父类,统一处理关于异常的操作,编写BaseController类,统一处理异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.cy.store.controller;

import com.cy.store.service.ex.InsertException;
import com.cy.store.service.ex.ServiceException;
import com.cy.store.service.ex.UsernameDuplicstedException;
import com.cy.store.util.JasonResult;
import org.springframework.web.bind.annotation.ExceptionHandler;

/*
控制层类的基类
*/
public class BaseController {
//操作成功的状态码
public static final int OK = 200;

//请求处理方法,这个方法的返回值为传递给前端的数据
//自动将异常对象传递给参数列表上
//项目产生异常自动拦截到此方法中,方法返回值直接给到前端
@ExceptionHandler(ServiceException.class) //用于统一处理抛出的异常
public JasonResult<Void> handleException(Throwable e){
JasonResult<Void> result = new JasonResult<>(e);
if(e instanceof UsernameDuplicstedException){
result.setState(4000);
result.setMessage("用户名被占用");
}else if (e instanceof InsertException){
result.setState(5000);
result.setMessage("注册时产生未知异常");
}
return result;
}
}


重构reg()

1
2
3
4
5
6
7
8
9
10
@RequestMapping("reg")
public JasonResult<Void> reg(User user){
//创建响应对象
JasonResult<Void> result =new JasonResult<>();

userService.reg(user);

return new JasonResult<>(OK);
}

前端页面

1.在这个页面中编写发送请求的方法,点击事件来完成.选中对应的按钮($(“选择器”)),再去添加点击事件,$.ajax()发送异步请求

2.JQUery封装一个函数,称为$.ajax(),通过对象调用ajax()函数,异步加载相关请求.依靠XHR(XmlHttp Response)

3.ajax()使用方式,需要传递方法作为方法参数,一对大括号称为方法体,

ajax接收多个参数,要求以”,”分割,每组参数以”:”分割,参数组成部分为参数名称(固定的),是参数的值,用字符串标识,无顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$.ajax({
url: "",
type: "",
data: "",
dataType: "",
sucess: function(){

},
error: function(){

}
});



参数 功能描述
url 标识请求地址ex : url: “localhost:8080/users/reg”
type 请求类型,ex: type:”POST”
data 提交的数据 data: “username=tom&pwd=123456”
datatype 数据类型,一般为json dataType: “json”
sucess 服务器正常响应时调用sucess方法,并将服务器返回的数据传递到这个方法
error 不正常时调用error
  1. js代码可以独立声明在一个js文件中或script标签中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<script type="text/javascript">
//监听注册按钮被点击
$("#btn-reg").click(function (){
let username = $("#username").val();
let passwd = $("#passwd").val();
let mail = $("#email").val();
//发送ajax异步请求
$.ajax({
url: "/users/reg",
type: "POST",
data: "username="+username+"&password="+passwd+"&mail="+mail,
dataType: "JSON",
success: function (json){
if(json.state == 200) {
alert("注册成功");
}else {
alert("注册失败");
}
},
error: function (){
alert("注册时产生未知错误!"+xhr.status);
}
});
});
</script>

用户登录

用户输入用户名密码,将数据交给后台查询,若查到登陆成功,条状到主页

持久层

规划sql语句

密码比较在业务层

1
2
select * from t_user where username = ?

某一个功能模块已经被开发完成,省略当前开发步骤

接口设计与方法

业务层

规划异常

1.用户名对应密码错误,密码匹配错误: PasswordNotMatchException异常,运行时,业务异常

2.用户名没有被找到,抛出异常:UsernameNotFoundException.

3.异常的编写:

业务层继承ServiceException异常类

在具体异常类中定义构造方法

设计业务层接口和抽象方法

1.直接在UserService接口编写抽象方法login(username,password), 将当前数据以当前用户对象形式返回.

状态管理,将数据保存在cookie或session中,避免重复多次请求

id存放在session中,头像在cookie

2.需要在实现类中实现父接口抽象方法

3.测试

抽象方法实现

控制层

处理异常

业务层抛出的异常是什么,需要在统一异常处理类中统一捕获,处理.已经处理过不必添加

1
2
3
4
5
6
7
8
else if (e instanceof UsernameNotFoundException){
result.setState(5001);
result.setMessage("用户数据不存在异常");
}else if (e instanceof PasswordNotMatchException){
result.setState(5002);
result.setMessage("用户名密码错误异常");
}

设计请求
1
2
3
4
5
请求路径: users/login
请求参数: String username,String password
请求类型: post
响应结果: JavaResult<void>

处理请求

在UserControl中编写方法

前端页面

用户会话session

session存储在服务器端,保存临时数据对象,在整个项目都可以访问,把session看作一个共享的数据.首次登录获取的用户数据,转移到session.

session.getAttrbute(“key”)获取session数据进行封装,在BaseController.

1.封装session对象中数据获取(父类)和设置(当用户登陆成功后进行数据设置,全局session对象).

2.在父类中封装两个数据:获取uid和username对应两个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
获取session的uid
*/
protected final Integer getidFromSession(HttpSession session){
return Integer.valueOf(session.getAttribute("id").toString());
}
/*
获取username

*/
protected final String getUsernameFromSession(HttpSession session){
return session.getAttribute("username").toString();
}

3.在登录中将数据封装在session对象中,服务器自动创建session.

SpringBoot直接使用session,直接将HttpSession类型对象作为请求处理方法参数,自动注入到形参上

拦截器

拦截器:将所有请求统一拦截到拦截器中,可以在拦截器中定义过滤规则,不满足过滤规则,重新打开login.html页面(重定向,转发)

Springboot项目拦截器定义与使用.

springMVC提供HandlerInterceptor表示拦截器

1
2
3
4
5
6
7
8
9
10
11
12
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}

default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}

default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}

1.LoginInterceptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.cy.store.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;

public class LoginInterceptor implements HandlerInterceptor {
/*
检测session中是否有id,有则放行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object obj = request.getSession().getAttribute("id");
if(obj == null){
//未登录,不放
response.sendRedirect("/web/login.html");
return false;
}
//请求放行
return true;
}
}


2.添加白名单(login.html/register.html/index.html),添加黑名单

3.注册过滤器:WebMvcConfigure,将用户定义的拦截器注册,定义一个类,实现该接口.存放在config上

1
2
3
default void addInterceptors(InterceptorRegistry registry) {
}

4.重定向次数过多