目录
1. 顺序图
2. 参数要求
3. 创建 Service 接口
4. 实现 Service 接口
5. 单体测试
6. 实现 Controller
7. 实现前端
在用户登录部分特别注意的是需要进行密码校验:
1. MD5(MD5(用户提交的原密码)+数据库查出来的用户的盐)= 密码的密文
2. 用上面的生成的密码的密文和数据库中用户的 password 字段的密码作比较,如果相等,则校验通过,否则失败。
1. 顺序图
2. 参数要求
登录时需要用户提交的参数列表:
参数名 | 描述 | 类型 | 默认值 | 条件 |
---|---|---|---|---|
username | 用户名 | String | 必须 | |
password | 密码 | String | 必须 |
3. 创建 Service 接口
根据用户名查询用户信息:
public interface IUserService {
/**
* 根据用户名查询用户信息
* @param username 用户名
* @return
*/
User selectByName(String username);
/**
* 创建普通用户
*
* @param user 用户名
*/
void createNormalUser(User user);
/**
* ⽤⼾登录
* @param username ⽤⼾名
* @param password 密码
* @return
*/
User login(String username, String password);
}
4. 实现 Service 接口
@Slf4j // 日志
@Service // 交给 Spring 管理
public class UserServiceImpl implements IUserService {
@Resource
private UserMapper userMapper;
@Override
public User selectByName(String username) {
// 非空校验
if(StringUtils.isEmpty(username)){
// 打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
// 根据用户名查询用户信息
User user = userMapper.selectByName(username);
// 返回结果
return user;
}
@Override
public void createNormalUser(User user) {
// 非空校验
if(user == null || StringUtils.isEmpty(user.getUsername())
|| StringUtils.isEmpty(user.getNickname())
|| StringUtils.isEmpty(user.getPassword())
|| StringUtils.isEmpty(user.getSalt())){
// 打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
// 校验用户是否存在
User existUser = selectByName(user.getUsername());
if(existUser != null){
// 打印日志
log.warn(ResultCode.FAILED_USER_EXISTS.toString() + "username = " + user.getUsername());
// 抛出异常,用户已存在
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_EXISTS));
}
// 为属性设置默认值
// 性别 0女 1男 2保密
if(user.getGender() != null){
if(user.getGender() < 0 || user.getGender() > 2){
user.setGender((byte)2);
}
}else{
user.setGender((byte)2);
}
// 发帖数
user.setArticleCount(0);
// 是否管理员
user.setIsAdmin((byte)0);
// 状态
user.setState((byte)0);
// 时间
Date date = new Date();
user.setCreateTime(date); // 创建时间
user.setUpdateTime(date); // 更新时间
// 写入数据库
int row = userMapper.insertSelective(user);
if(row != 1){
// 打印日志
log.warn(ResultCode.FAILED_CREATE.toString() + "注册用户失败,username = " + user.getUsername());
// 抛出异常,用户已存在
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_CREATE));
}
}
@Override
public User login(String username, String password) {
// 非空校验
if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){
// 打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
// 抛出异常,用户已存在
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
// 根据用户名查询用户信息
User user = selectByName(username);
// 校验用户是否存在
if (user == null) {
// 打印日志
log.info(ResultCode.FAILED_USER_NOT_EXISTS.toString()+",username = " + username);
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_LOGIN));
}
// 校验密码是否正确
String encryptPassword = MD5Utils.md5Salt(password, user.getSalt());
if (!encryptPassword.equalsIgnoreCase(user.getPassword())) {
// 打印日志
log.info("密码输入错误,username = " + username + ", password = " + password);
// 密码不正确抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_LOGIN));
}
// 校验通过,返回用户信息
return user;
}
}
5. 单体测试
编写测试代码:
@Test
void login() throws JsonProcessingException {
// 正确用户
User user = userService.login("Danny","123");
System.out.println(objectMapper.writeValueAsString(user));
// 错误用户
user = userService.login("123","123");
System.out.println(objectMapper.writeValueAsString(user));
}
测试结果如下:
6. 实现 Controller
在 UserControlle r中实现登录方法:
因此在 Config 包下新建 AppConfig 类,添加全局变量:
public class AppConfig {
/**
* 为 session 中存储的 User 对象定义一个全局变量
*/
public static final String SESSION_USER_KEY = "SESSION_USER_KEY";
}
继续编写 UserController 类中的方法:
/**
* 用户登录
*
* @param request
* @param username 用户名
* @param password 密码
* @return AppResult
*/
@ApiOperation("⽤⼾登录")
@PostMapping("/login")
public AppResult login (HttpServletRequest request,
@ApiParam(value = "用户名") @RequestParam(value = "username") @NonNull String username,
@ApiParam(value = "密码") @RequestParam(value = "password") @NonNull String password) {
// 调用 Service
User user = userService.login(username, password);
// 在 session 中保存当前登录的用户信息
// 1. 获取 session 对象
HttpSession session = request.getSession(true);
// 2. 把用户信息保存在 session 中
session.setAttribute(AppConfig.SESSION_USER_KEY, user);
// 登录成功响应
return AppResult.success("登录成功");
}
启动程序,访问 API 页面 login 接口,分别提交错误和正确的用户名密码进行测试:
登录失败:
登陆成功:
7. 实现前端
完整的前端代码:forum: 论坛项目 - Gitee.com
// 构造数据
let postData = {
username : $('#username').val(),
password : $('#password').val()
};
// 发送AJAX请求,成功后跳转到index.html
$.ajax({
// 请求的方法类型
type : 'POST',
// API 的 URL
url : 'user/login',
//数据格式
contentType : 'application/x-www-form-urlencoded',
// 提交的数据
data : postData,
// 成功回调
success : function(respData) {
// ⽤状态码判断是否成功
if (respData.code == 0) {
// 成功
location.assign('index.html');
} else {
// 失败
$.toast({
heading : '警告',
text : respData.message,
icon : 'Warning'
});
}
},
// 失败(HTTP)
error: function() {
$.toast({
heading : '错误',
text : '出错了,请联系管理员',
icon : 'error'
});
}
});
});
登录后的界面如下:
以上即为登录功能的实现,接下来在下篇博客中我们将实现在上面登录进去的界面中进行退出。