UserServiceImpl.java类中添加以下两段代码:
// 用户名未被占用,允许注册
// 向参数user中补全属性:盐值
String salt = UUID.randomUUID().toString().toUpperCase();
user.setSalt(salt);
// 取出参数user中的原始密码
String password = user.getPassword();
// 将原始密码加密
String md5Password = getMd5Password(password, salt);
// 向参数user中补全属性:加密后的密码
user.setPassword(md5Password);
// 向参数user中补全属性:isDelete-0
user.setIsDelete(0);
多重加密
/**
* 执行密码加密,获取加密后的密码
* @param password 原密码
* @param salt 盐值
* @return 加密后的密码
*/
private String getMd5Password(String password, String salt) {
// 加密规则:使用“salt+password+salt”作为消息,执行3次摘要运算
String str = salt + password + salt;
for (int i = 0; i < 3; i++) {
str = DigestUtils.md5DigestAsHex(str.getBytes()).toUpperCase();
}
return str;
}
#################### 5.1 #####################
1.UserServiceImpl.java类中,添加密码加密和方法。
2.在业务层测试类中添加测试。
效果为:
1.控制台输出注册成功
2.数据库中密码显示是加密的。
6. 用户-注册-控制器层
(a)统一处理异常
创建类’cn.tedu.store.controller.BaseController’控制类的基类,
后续创建的每个控制器都应该继承这个基类,在基类中添加处理异常的方法,
则每个子级都拥有这个方法:
public abstract class BaseController {
@ExceptionHandler(ServiceException.class)
@ResponseBody
public JsonResult<Void> handleException(Throwable e) {
JsonResult<Void> jr = new JsonResult<Void>();
jr.setMessage(e.getMessage());
if (e instanceof UsernameDuplicateException) {
jr.setState(2);
} else if (e instanceof InsertException) {
jr.setState(3);
}
return jr;
}
}
}
(b) 设计请求
设计“用户注册”的请求方式:
请求路径:/users/reg
请求参数:User user
请求方式:POST
响应数据:JsonResult<Void>
© 处理请求
1.首先,需要创建cn.tedu.store.util.JsonResult
响应结果类型:
public class JsonResult<T> {
private Integer state;
private String message;
private T data;
// SET/GET
}
2.再需要创建cn.tedu.store.controller.UserController
控制器类,
在类之前添加@RestController
和@RequestMapping("users")
这2个
注解,在类中添加声明@Autowired private IUserService userService;
业务层对象:
@RestController
@RequestMapping("users")
public class UserController extends xxxController {
@Autowired
private IUserService userService;
}
3.接下来,在类中添加处理请求的方法:
@RequestMapping("reg")
public JsonResult<Void> reg(User user) {
JsonResult<Void> jr = new JsonResult<Void>();
userService.reg(user);
jr.setState(1);
return jr;
}
完成后,启动项目,打开浏览器,通过http://localhost:8080/users/reg? username=root&password=1234
进行测试。
测试完成后,将方法之前的@RequestMapping
替换为@PostMapping
。
(自己进行限制,也可以项目差不多完成之后再改成post请求的方式。)
#################### 以上大纲步骤 1 #####################
1.补充昨天的密码密码功能
2.添加控制层基类
3.添加响应结果的包和类
4.添加用户控制类
5. 测试效果为:
浏览器显示:
{“state”:1,“message”:null,“data”:null}
刷新后:
{"state":2,"message":"注册失败!尝试注册的用户名(root)已经被占用!","data":null}
#################### 代码调整 #####################
1.BaseController.java类中添加 操作成功时的响应状态代号
修改代码:
JsonResult jr = new JsonResult();
修改为:
JsonResult<Void> jr = new JsonResult<>();
2.JsonResult.java类中添加state构造方法
public JsonResult(Integer state) {
super();
this.state = state;
}
再添加一个无参的构造方法
3.JsonResult.java类中再添加一个message的构造方法
并修改为最大异常;
public JsonResult(Throwable e) {
super();
this.message = e.getMessage();
}
BaseController.java类中的:
JsonResult<Void> jr = new JsonResult<>();
则可以修改为(多加了个e):
JsonResult<Void> jr = new JsonResult<>(e);
4.UserController.java类中:
修改代码:
@RequestMapping("reg")
public JsonResult<Void> reg(User user) {
JsonResult<Void> jr = new JsonResult<Void>();
userService.reg(user);
jr.setState(1);
return jr;
}
修改为:
@RequestMapping("reg")
public JsonResult<Void> reg(User user) {
userService.reg(user);
return new JsonResult<>(SUCCESS);
}
5.最后BaseController.java类中再添加代码
/**
* 操作成功时的响应状态代号
*/
protected static final Integer SUCCESS = 2000;
if (e instanceof UsernameDuplicateException) {
jr.setState(4000);
} else if (e instanceof InsertException) {
jr.setState(5000);
}
6.浏览器还是输入http://localhost:8080/users/reg?
username=root&password=1234并测试
效果为:2000和用户名重复:4000
7.项目完成之后需要将 UserController.java类中的权限改成@PostMapping
测试阶段可以暂时不修改
7. 用户-注册-前端界面
1.复制dom网页中学子商城下载的五个文件夹,放到项目static文加夹下
a.Ajax代码复制
b.register.htnl注册页面四个地方需要调整
(a)有以下代码可以删除掉,不需要。
$("#span-username").html("");
(b)修改url路径
(c)修改以下代码
if(obj.state == 2000){
alert("注册成功");
} else {
alert(obj.message);
}
(d)修改表单id为"form-reg"
名字和密码的input里面都要加上name=“相应的值”
再次输入密码检查是客户端的事情,服务端不管。
(e)立即注册按钮加上id="btn-reg"
具体的四个地方为:表单id、用户名和 密码的name 立即注册的id四个。
注意:老师导入项目:则需要四个前端页面的文件包复制进来,
不然则前端页面运行不了。
最后在浏览器输入前端页面的url进行测试:http://localhost:8080/web/Register.html
#################### 以上大纲步骤 2 #####################
1.修改UserMapper.xml中的查询代码。
2.UserMapperTests类中测试。
测试效果:控制台输出查询的数据
数据库中也能查到对应的数据。
8. 用户-登录-持久层
(a) 规划SQL语句
用户登录时,需要检查用户名是否存在,密码是否匹配,且is_delete
的值是否表示“未删除”,关于“用户名”是否存在,可以通过查询数据库得到:
select * from t_user where username=?
如果查询到有效结果,则用户名是存在,可以进行后续的验证,如果查询的结果是null
,则用户名尚未注册,不允许使用这个用户名登录!
关于密码、is_delete
的验证应该在后续的业务层中进行判断,只需要保证以上查询时,能够查到与验证相关的字段即可,例如:
select password, salt, is_delete from t_user where username=?
并且,如果用户登录成功,还应该将用户(客户端)提供用户的相关数据,例如用户的用户名、头像,还需要在Session中记录该用户的id和用户名,则查询时还要查询这些字段:
select uid, username, password, salt, avatar, is_delete from t_user where username=?
(b) 接口与抽象方法
在UserMapper.java
接口中已经存在User findByUsername(String username)
方法,则无需重复定义抽象方法。
© 配置映射
此次需要在findByUsername()
方法的映射中补充查询更多的字段:
<!-- 根据用户名查询用户数据 -->
<!-- User findByUsername(String username) -->
<select id="findByUsername"
resultType="cn.tedu.store.entity.User">
SELECT
uid, username,
password, salt,
avatar, is_delete AS isDelete
FROM
t_user
WHERE
username=#{username}
</select>
完成后,再次执行已经存在的单元测试。
9. 用户-登录-业务层
(a) 规划异常
此时,应该穷举用户在“登录”过程中可能出现的任何“错误”,例如,可能出现“用户名不存在”,或“密码错误”,或“用户数据被标记为已删除”。
所以,需要为以上“错误”创建对应的异常类:
cn.tedu.store.service.ex.UserNotFoundException
cn.tedu.store.service.ex.PasswordNotMatchException
创建的异常类都应该继承自ServiceException
。
(b) 接口与抽象方法
在IUserService
接口中添加“登录”的抽象方法:
User login(String username, String password) throws UserNotFoundException, PasswordNotMatchException;
© 实现抽象方法
在UserServiceImpl
实现类中重写以上抽象方法:
@Override
public User login(String username, String password) throws UserNotFoundException, PasswordNotMatchException {
// 根据参数username执行查询
User result = userMapper.findByUsername(username);
// 判断查询结果是否为null
if (result == null) {
// 是:抛出UserNotFoundException
throw new UserNotFoundException(
"登录失败!用户数据不存在!");
}
// 判断查询结果中的isDelete是否为1
if (result.getIsDelete() == 1) {
// 是:抛出UserNotFoundException
throw new UserNotFoundException(
"登录失败!用户数据不存在!");
}
// 从查询结果中获取盐值
String salt = result.getSalt();
// 基于参数password和盐值执行加密
String md5Password = getMd5Password(password, salt);
// 判断以上加密结果与查询结果中的password是否不匹配
if (!md5Password.equals(result.getPassword())) {
// 是:抛出PasswordNotMatchException
throw new PasswordNotMatchException(
"登录失败!密码错误!");
}
// 将查询结果中的password设置为null
result.setPassword(null);
// 将查询结果中的salt设置为null
result.setSalt(null);
// 将查询结果中的isDelete设置为null
result.setIsDelete(null);
// 返回查询结果
return result;
}
最后,在UserServiceTests
中编写并执行单元测试:
@Test
public void login() {
try {
String username = "root";
String password = "1234x";
User result = service.login(username, password);
System.err.println(result);
} catch (ServiceException e) {
System.err.println(e.getClass().getName());
System.err.println(e.getMessage());
}
}
10. 用户-登录-控制器层
(a) 统一处理异常
(b) 设计请求
© 处理请求