本文开发一用户注册与登录的后端java平台。开发完后的网页效果:http://user-front.66bond.com/
后端主要提供用户的注册、查询、删除等操作。使用到的开发工具:
IDEA2021; 关注公众号”青椒工具”,发送”IDEA”,获取windows下的IDEA安装包
mysql 5.7;关注公众号”青椒工具”,发送”mysql”,获取windows下的mysql5.7安装包;
用户登录的流程:
后台登录时,需要接受的参数:用户账户,密码;
网络请求类型:POST
返回值:返回当前用户的基本信息;数据要脱敏;不要返回相关隐私;返回json格式数据;
1、登录逻辑 与注册逻辑类似,登录的逻辑包括:
1、用户名和密码非空;
2、账户长度不小于4,密码不小于8,账户不包含特殊字符;
3、密码是否正确,需要查询数据库;
4、数据脱敏,敏感信息要隐藏,不能返回;
5、记录登录态,将用户登录状态保存在后端;
2、代码框架: 书写登录业务代码涉及到文件,与注册类似:
在service中的接口UserService.java中定义登录方法:
1 2 3 4 public interface UserService extends IService <User> { long userRegister (String userAccount, String userPassword, String checkPassword) ; User userLogin (String userAccount, String userPassword) ; }
在service的实现类中UserServiceImpl.java实现userLogin的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class UserServiceImpl extends ServiceImpl <UserMapper, User> implements UserService { @Override public long userRegister (String userAccount, String userPassword, String checkPassword) { return 0 ; } @Override public User userLogin (String userAccount, String userPassword) { } }
在test/java/com.tfzhang.backend/service/UserServiceTest.java中创建测试文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class UserServiceTest { @Resource private UserService userService; @Test public void testAddUser () { User user = new User (); } @Test void userRegister () { } @Test void userLogin () { } }
3、业务代码实现: 接下来主要在UserServiceImpl.java中实现各种逻辑的代码,其中与注册类似的代码,可以直接借用:
1、用户名和密码非空;
2、账户长度不小于4,密码不小于8,账户不包含特殊字符;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if (StringUtils.isAnyBlank(userAccount, userPassword)){ return null ; } if (userAccount.length() < 4 || userPassword.length()<8 ){ return null ; } String regex = "^[a-zA-Z0-9]+$" ;Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(userAccount);if (!matcher.matches()){ return null ; }
3、密码是否正确,需要查询数据库;
校验密码是否正确,注意数据库中保存的是加密后的数据,所以我们也要对密码数据先加密,然后再与数据库中的对比;
1 2 3 4 5 6 7 8 9 10 11 final String SALT = "tfzhang" ; String encrptedPassword = DigestUtils.md5DigestAsHex((SALT+userPassword).getBytes()); QueryWrapper<User> queryWrapper=new QueryWrapper <>(); queryWrapper.eq("userAccount" , userAccount); queryWrapper.eq("userPassword" , encrptedPassword); User user= userMapper.selectOne(queryWrapper); if (user == null ){ return null ; }
此处的userMapper对象要定义为私有类属性,并添加Resource标签;
1 2 @Resource private UserMapper userMapper;
4、数据脱敏,敏感信息要隐藏,不能返回;
登录返回用户的数据,其中的敏感数据要屏蔽,才能返回数据给前端。所谓的屏蔽只是调用下set方法。
1 2 3 4 5 6 7 8 9 10 User saftyUser=new User (); saftyUser.setId(user.getId()); saftyUser.setUsername(user.getUsername()); saftyUser.setUserAccount(user.getUserAccount()); saftyUser.setAvatarUlr(user.getAvatarUlr()); saftyUser.setGender(user.getGender()); saftyUser.setUserState(user.getUserState()); return saftyUser;
5、记录登录态,将用户登录状态保存在后端;
后端只有保存用户的登录态,才能知道连接时,该连接来自哪个用户,前端和后端用户登录态的交互过程如下:
连接服务器端后,得到一个 session 状态(匿名会话),返回给前端
登录成功后,得到了登录成功的 session,并且给该session设置一些值(比如用户信息),返回给前端一个设置 cookie 的 ”命令“
session => cookie
前端接收到后端的命令后,设置 cookie,保存到浏览器内
前端再次请求后端的时候(相同的域名),在请求头中带上cookie去请求
后端拿到前端传来的 cookie,找到对应的 session
后端从 session 中可以取出基于该 session 存储的变量(用户的登录信息、登录名)
后端的代码只要添加如下:
1 request.getSession().setAttribute('userLoginState' , safetyUser);
其中的request参数来自前端,需要修改userLogin方法的声名:
1 User userLogin (String account, String userPassword, HttpServletRequest request)
4、业务代码的优化: 将上述所有的业务代码片段整合后,得到如下:
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 @Resource private UserMapper userMapper; @Override public User userLogin (String userAccount, String userPassword, HttpServletRequest request) { if (StringUtils.isAnyBlank(userAccount, userPassword)){ return null ; } if (userAccount.length() < 4 || userPassword.length()<8 ){ return null ; } String regex = "^[a-zA-Z0-9]+$" ; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(userAccount); if (!matcher.matches()){ return null ; } final String SALT = "tfzhang" ; String encrptedPassword = DigestUtils.md5DigestAsHex((SALT+userPassword).getBytes()); QueryWrapper<User> queryWrapper=new QueryWrapper <>(); queryWrapper.eq("userAccount" , userAccount); queryWrapper.eq("userPassword" , encrptedPassword); User user= userMapper.selectOne(queryWrapper); if (user == null ){ return null ; } User saftyUser=new User (); saftyUser.setId(user.getId()); saftyUser.setUsername(user.getUsername()); saftyUser.setUserAccount(user.getUserAccount()); saftyUser.setAvatarUlr(user.getAvatarUlr()); saftyUser.setGender(user.getGender()); saftyUser.setUserStatus(user.getUserStatus()); request.getSession().setAttribute("userLoginState" , saftyUser); return saftyUser; }
仔细审视上述的代码,看看有没有优化的空间?
SALT变量不只在登录时用到,注册时也用到了;对于这种公共的变量有必要提取出来,统一管理;
“userLoginState”是固定的变量,不会有变动,适宜统一管理;
数据脱敏的代码有必要简单封装到一个独立的方法中,这样看起来更优雅;
对于上述的第一点,我们在/src/main/java/com.tfzhang.backend/下创建名为constant的package,其中再创建名为UserConstant的interface,其中的代码:
1 2 3 4 5 6 package com.tfzhang.backend.constant;public interface UserConstant { String USER_LOGIN_STATE="userLoginState" ; String SALT="tfzhang" ; }
然后再修改登录中的代码:
1 2 3 4 5 6 7 import static com.tfzhang.backend.constant.UserConstant.SALT;import static com.tfzhang.backend.constant.UserConstant.USER_LOGIN_STATE; String encrptedPassword = DigestUtils.md5DigestAsHex((SALT+userPassword).getBytes()); request.getSession().setAttribute(USER_LOGIN_STATE, safetyUser);
对于第2点,我们在当前文件夹下创建一个getSafetyUser方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public User getSafetyUser (User originalUser) { if (originalUser==null ){ return null ; } User user = new User (); user.setId(originalUser.getId()); user.setUsername(originalUser.getUsername()); user.setUserAccount(originalUser.getUserAccount()); user.setAvatarUlr(originalUser.getAvatarUlr()); user.setGender(originalUser.getGender()); user.setUserStatus(originalUser.getUserStatus()); return user; }
然后原来的代码直接调用上述的getSafetyUser即可。
5、数据库中用户的逻辑删除设置: 我们之前在设计数据库中有提到过一个逻辑删除的变量: isDelete
该变量表示当前的数据是否已经逻辑删除,逻辑删除是指:数据仍在数据库中,但是不会被查找;对外不可见。
比如:如果我们登录一个被逻辑删除的账号,那么即使账户名和密码都对,数据库也应该显示无该数据。那么mybatis-plus要做到的这一点,需要进行如下的配置(搜官方文档):
步骤1:配置application.yml
1 2 3 4 5 6 mybatis-plus: global-config: db-config: logic-delete-field: flag logic-delete-value: 1 logic-not-delete-value: 0
步骤2:实体字段上加注解;
1 2 @TableLogic private Integer deleted;
对照我们的项目,我们需要在application.yml文件中,添加如下的内容:
1 2 3 4 5 6 mybatis-plus: global-config: db-config: logic-delete-field: isDelete logic-delete-value: 1 logic-not-delete-value: 0
在User.java中添加注解:
1 2 @TableLogic private Integer isDelete;
登录部分的测试代码我们不写了,因为与注册基本类似,下面我们写controller层的网络访问接口代码。
6、参考资料: 本文参考自如下知识星球中的视频教程,更多的完整的相关视频教程,见如下的收费 知识星球,近3万人的学习社区,
编程有人同行,学习不再迷茫 :