SpringBoot写一个登陆注册功能,和期间走的坑

时间:2023-03-09 05:05:17
SpringBoot写一个登陆注册功能,和期间走的坑

前言

   最近在做一个关于SpringBoot的项目,首先从最简单的注册登陆开始,从前端到后端。

1. 首先介绍项目的相关技术和工具:

开发工具使用IDEA,技术使用SpringBoot2.1.3+Mybatis+Jpa+mysql,项目中主要使用Mybatis,jpa只做了demo,实体转换使用的是mapstruct,集成了swagger文档配置,redis缓存demo。

2. 首先创建项目

两种方式:1、直接在IDEA中file–>new–>project,选择spring Initalizr创建一个springBoot项目。

SpringBoot写一个登陆注册功能,和期间走的坑

2、或者直接在spring的官网创建一个springboot项目springBoot官网创建项目

3. 项目的结构

首先项目的结构和普通的spring项目是一样的,采用controller、service、dao三层

SpringBoot写一个登陆注册功能,和期间走的坑

接下来项目采用逆向介绍:

实体—>Mapper.xml—>Mapper.inteface–>service–>controller—>html

项目中的一些实体和mybatis的代码,采用mybatis generator逆向生成。

如果逆向生成不太懂,请自行百度了解或点击Mybatis-Generator之最完美配置详解

3.1实体类:

@Table(name = "user_info")
@Entity
public class UserInfo extends BaseEntity{ @Id
@Column(name = "USER_ID")
private Long userId; @Column(name = "USER_NICK_NAME")
private String userNickName; @Column(name = "USER_SUR_NAME")
private String userSurName; @Column(name = "USER_NAME")
private String userName; @Column(name = "USER_DESC")
private String userDesc; @Column(name = "USER_SEX")
private Boolean userSex; @Column(name = "USER_PHONE")
private String userPhone; @Column(name = "USER_EMAIL")
private String userEmail; @Column(name = "USER_HOME")
private String userHome; @Column(name = "USER_BIRTHDAY")
private Date userBirthday; @Column(name = "USER_REGISTER_DATE")
private Date userRegisterDate; @Column(name = "USER_LEVEL")
private Long userLevel; @Column(name = "PASS_WORD")
private String passWord; @Column(name = "LOGIN_NUM")
private Long loginNum;
GetSet方法省略

由于项目中集成了Jpa所以实体类上有表和列的注解。

3.2 Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.dao.mybatis.UserInfoMapper">
<resultMap id="BaseResultMap" type="com.example.demo.entity.UserInfo">
<id column="USER_ID" jdbcType="BIGINT" property="userId" />
<result column="USER_NICK_NAME" jdbcType="VARCHAR" property="userNickName" />
<result column="USER_SUR_NAME" jdbcType="VARCHAR" property="userSurName" />
<result column="USER_NAME" jdbcType="VARCHAR" property="userName" />
<result column="USER_DESC" jdbcType="VARCHAR" property="userDesc" />
<result column="USER_SEX" jdbcType="BIT" property="userSex" />
<result column="USER_PHONE" jdbcType="VARCHAR" property="userPhone" />
<result column="USER_EMAIL" jdbcType="VARCHAR" property="userEmail" />
<result column="USER_HOME" jdbcType="VARCHAR" property="userHome" />
<result column="USER_BIRTHDAY" jdbcType="TIMESTAMP" property="userBirthday" />
<result column="USER_REGISTER_DATE" jdbcType="TIMESTAMP" property="userRegisterDate" />
<result column="CREATE_DATE" jdbcType="TIMESTAMP" property="createDate" />
<result column="UPDATE_DATE" jdbcType="TIMESTAMP" property="updateDate" />
<result column="STATUS_CD" jdbcType="DECIMAL" property="statusCd" />
<result column="STATUS_DATE" jdbcType="TIMESTAMP" property="statusDate" />
<result column="REMARK" jdbcType="VARCHAR" property="remark" />
<result column="USER_LEVEL" jdbcType="BIGINT" property="userLevel" />
<result column="PASS_WORD" jdbcType="VARCHAR" property="passWord" />
<result column="LOGIN_NUM" jdbcType="BIGINT" property="loginNum" />
</resultMap>
<sql id="Base_Column_List">
USER_ID, USER_NICK_NAME, USER_SUR_NAME, USER_NAME, USER_DESC, USER_SEX, USER_PHONE,
USER_EMAIL, USER_HOME, USER_BIRTHDAY, USER_REGISTER_DATE, CREATE_DATE, UPDATE_DATE,
STATUS_CD, STATUS_DATE, REMARK, USER_LEVEL, PASS_WORD, LOGIN_NUM
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from user_info
where USER_ID = #{userId,jdbcType=BIGINT}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
delete from user_info
where USER_ID = #{userId,jdbcType=BIGINT}
</delete>
<insert id="insert" parameterType="com.example.demo.entity.UserInfo">
insert into user_info (USER_ID, USER_NICK_NAME, USER_SUR_NAME,
USER_NAME, USER_DESC, USER_SEX,
USER_PHONE, USER_EMAIL, USER_HOME,
USER_BIRTHDAY, USER_REGISTER_DATE, CREATE_DATE,
UPDATE_DATE, STATUS_CD, STATUS_DATE,
REMARK, USER_LEVEL, PASS_WORD,
LOGIN_NUM)
values (#{userId,jdbcType=BIGINT}, #{userNickName,jdbcType=VARCHAR}, #{userSurName,jdbcType=VARCHAR},
#{userName,jdbcType=VARCHAR}, #{userDesc,jdbcType=VARCHAR}, #{userSex,jdbcType=BIT},
#{userPhone,jdbcType=VARCHAR}, #{userEmail,jdbcType=VARCHAR}, #{userHome,jdbcType=VARCHAR},
#{userBirthday,jdbcType=TIMESTAMP}, #{userRegisterDate,jdbcType=TIMESTAMP}, #{createDate,jdbcType=TIMESTAMP},
#{updateDate,jdbcType=TIMESTAMP}, #{statusCd,jdbcType=DECIMAL}, #{statusDate,jdbcType=TIMESTAMP},
#{remark,jdbcType=VARCHAR}, #{userLevel,jdbcType=BIGINT}, #{passWord,jdbcType=VARCHAR},
#{loginNum,jdbcType=BIGINT})
</insert>
<insert id="insertSelective" parameterType="com.example.demo.entity.UserInfo">
insert into user_info
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userId != null">
USER_ID,
</if>
<if test="userNickName != null">
USER_NICK_NAME,
</if>
<if test="userSurName != null">
USER_SUR_NAME,
</if>
<if test="userName != null">
USER_NAME,
</if>
<if test="userDesc != null">
USER_DESC,
</if>
<if test="userSex != null">
USER_SEX,
</if>
<if test="userPhone != null">
USER_PHONE,
</if>
<if test="userEmail != null">
USER_EMAIL,
</if>
<if test="userHome != null">
USER_HOME,
</if>
<if test="userBirthday != null">
USER_BIRTHDAY,
</if>
<if test="userRegisterDate != null">
USER_REGISTER_DATE,
</if>
<if test="createDate != null">
CREATE_DATE,
</if>
<if test="updateDate != null">
UPDATE_DATE,
</if>
<if test="statusCd != null">
STATUS_CD,
</if>
<if test="statusDate != null">
STATUS_DATE,
</if>
<if test="remark != null">
REMARK,
</if>
<if test="userLevel != null">
USER_LEVEL,
</if>
<if test="passWord != null">
PASS_WORD,
</if>
<if test="loginNum != null">
LOGIN_NUM,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">
#{userId,jdbcType=BIGINT},
</if>
<if test="userNickName != null">
#{userNickName,jdbcType=VARCHAR},
</if>
<if test="userSurName != null">
#{userSurName,jdbcType=VARCHAR},
</if>
<if test="userName != null">
#{userName,jdbcType=VARCHAR},
</if>
<if test="userDesc != null">
#{userDesc,jdbcType=VARCHAR},
</if>
<if test="userSex != null">
#{userSex,jdbcType=BIT},
</if>
<if test="userPhone != null">
#{userPhone,jdbcType=VARCHAR},
</if>
<if test="userEmail != null">
#{userEmail,jdbcType=VARCHAR},
</if>
<if test="userHome != null">
#{userHome,jdbcType=VARCHAR},
</if>
<if test="userBirthday != null">
#{userBirthday,jdbcType=TIMESTAMP},
</if>
<if test="userRegisterDate != null">
#{userRegisterDate,jdbcType=TIMESTAMP},
</if>
<if test="createDate != null">
#{createDate,jdbcType=TIMESTAMP},
</if>
<if test="updateDate != null">
#{updateDate,jdbcType=TIMESTAMP},
</if>
<if test="statusCd != null">
#{statusCd,jdbcType=DECIMAL},
</if>
<if test="statusDate != null">
#{statusDate,jdbcType=TIMESTAMP},
</if>
<if test="remark != null">
#{remark,jdbcType=VARCHAR},
</if>
<if test="userLevel != null">
#{userLevel,jdbcType=BIGINT},
</if>
<if test="passWord != null">
#{passWord,jdbcType=VARCHAR},
</if>
<if test="loginNum != null">
#{loginNum,jdbcType=BIGINT},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.example.demo.entity.UserInfo">
update user_info
<set>
<if test="userNickName != null">
USER_NICK_NAME = #{userNickName,jdbcType=VARCHAR},
</if>
<if test="userSurName != null">
USER_SUR_NAME = #{userSurName,jdbcType=VARCHAR},
</if>
<if test="userName != null">
USER_NAME = #{userName,jdbcType=VARCHAR},
</if>
<if test="userDesc != null">
USER_DESC = #{userDesc,jdbcType=VARCHAR},
</if>
<if test="userSex != null">
USER_SEX = #{userSex,jdbcType=BIT},
</if>
<if test="userPhone != null">
USER_PHONE = #{userPhone,jdbcType=VARCHAR},
</if>
<if test="userEmail != null">
USER_EMAIL = #{userEmail,jdbcType=VARCHAR},
</if>
<if test="userHome != null">
USER_HOME = #{userHome,jdbcType=VARCHAR},
</if>
<if test="userBirthday != null">
USER_BIRTHDAY = #{userBirthday,jdbcType=TIMESTAMP},
</if>
<if test="userRegisterDate != null">
USER_REGISTER_DATE = #{userRegisterDate,jdbcType=TIMESTAMP},
</if>
<if test="createDate != null">
CREATE_DATE = #{createDate,jdbcType=TIMESTAMP},
</if>
<if test="updateDate != null">
UPDATE_DATE = #{updateDate,jdbcType=TIMESTAMP},
</if>
<if test="statusCd != null">
STATUS_CD = #{statusCd,jdbcType=DECIMAL},
</if>
<if test="statusDate != null">
STATUS_DATE = #{statusDate,jdbcType=TIMESTAMP},
</if>
<if test="remark != null">
REMARK = #{remark,jdbcType=VARCHAR},
</if>
<if test="userLevel != null">
USER_LEVEL = #{userLevel,jdbcType=BIGINT},
</if>
<if test="passWord != null">
PASS_WORD = #{passWord,jdbcType=VARCHAR},
</if>
<if test="loginNum != null">
LOGIN_NUM = #{loginNum,jdbcType=BIGINT},
</if>
</set>
where USER_ID = #{userId,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKey" parameterType="com.example.demo.entity.UserInfo">
update user_info
set USER_NICK_NAME = #{userNickName,jdbcType=VARCHAR},
USER_SUR_NAME = #{userSurName,jdbcType=VARCHAR},
USER_NAME = #{userName,jdbcType=VARCHAR},
USER_DESC = #{userDesc,jdbcType=VARCHAR},
USER_SEX = #{userSex,jdbcType=BIT},
USER_PHONE = #{userPhone,jdbcType=VARCHAR},
USER_EMAIL = #{userEmail,jdbcType=VARCHAR},
USER_HOME = #{userHome,jdbcType=VARCHAR},
USER_BIRTHDAY = #{userBirthday,jdbcType=TIMESTAMP},
USER_REGISTER_DATE = #{userRegisterDate,jdbcType=TIMESTAMP},
CREATE_DATE = #{createDate,jdbcType=TIMESTAMP},
UPDATE_DATE = #{updateDate,jdbcType=TIMESTAMP},
STATUS_CD = #{statusCd,jdbcType=DECIMAL},
STATUS_DATE = #{statusDate,jdbcType=TIMESTAMP},
REMARK = #{remark,jdbcType=VARCHAR},
USER_LEVEL = #{userLevel,jdbcType=BIGINT},
PASS_WORD = #{passWord,jdbcType=VARCHAR},
LOGIN_NUM = #{loginNum,jdbcType=BIGINT}
where USER_ID = #{userId,jdbcType=BIGINT}
</update>
<select id="findByExample" parameterType="com.example.demo.entity.UserInfo"
resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from user_info
<where>
<if test="userNickName != null">
AND USER_NICK_NAME = #{userNickName,jdbcType=VARCHAR}
</if>
<if test="userSurName != null">
AND USER_SUR_NAME = #{userSurName,jdbcType=VARCHAR}
</if>
<if test="userName != null">
AND USER_NAME = #{userName,jdbcType=VARCHAR}
</if>
<if test="userDesc != null">
AND USER_DESC = #{userDesc,jdbcType=VARCHAR}
</if>
<if test="userSex != null">
AND USER_SEX = #{userSex,jdbcType=BIT}
</if>
<if test="userPhone != null">
AND USER_PHONE = #{userPhone,jdbcType=VARCHAR}
</if>
<if test="userEmail != null">
AND USER_EMAIL = #{userEmail,jdbcType=VARCHAR}
</if>
<if test="userHome != null">
AND USER_HOME = #{userHome,jdbcType=VARCHAR}
</if>
<if test="userBirthday != null">
AND USER_BIRTHDAY = #{userBirthday,jdbcType=TIMESTAMP}
</if>
<if test="userRegisterDate != null">
AND USER_REGISTER_DATE = #{userRegisterDate,jdbcType=TIMESTAMP}
</if>
<if test="createDate != null">
AND CREATE_DATE = #{createDate,jdbcType=TIMESTAMP}
</if>
<if test="updateDate != null">
AND UPDATE_DATE = #{updateDate,jdbcType=TIMESTAMP}
</if>
<if test="statusCd != null">
AND STATUS_CD = #{statusCd,jdbcType=DECIMAL}
</if>
<if test="statusDate != null">
AND STATUS_DATE = #{statusDate,jdbcType=TIMESTAMP}
</if>
<if test="remark != null">
AND REMARK = #{remark,jdbcType=VARCHAR}
</if>
<if test="userLevel != null">
AND USER_LEVEL = #{userLevel,jdbcType=BIGINT}
</if>
<if test="passWord != null">
AND PASS_WORD = #{passWord,jdbcType=VARCHAR}
</if>
<if test="loginNum != null">
AND LOGIN_NUM <![CDATA[ > ]]> #{loginNum,jdbcType=BIGINT}
</if>
</where>
</select>
</mapper>

关于这个xml多说一句,在代码运行期间遇到的坑:如果数据库表字段和实体的类型匹配不到,在结果返回的时候使用resultType是不能将结果正常的返回出来的,我遇到的情况是查询出数据条数为1,结果的list的size的确是1,但是里面却是null,这个就是因为字段类型不匹配,导致的数据没有被填充到指定的实体中,这个时候应该使用resultMap对表字段和实体字段进行指定。这样就能正常的返回数据了。

3.3 mapper.inteface

@Mapper
public interface UserInfoMapper{
int deleteByPrimaryKey(Long userId); int insert(UserInfo record); int insertSelective(UserInfo record); UserInfo selectByPrimaryKey(Long userId); int updateByPrimaryKeySelective(UserInfo record); int updateByPrimaryKey(UserInfo record); List<UserInfo> findByExample(UserInfo userInfo);
}

这里说一句注意点,这个接口是用来和xml文件进行交互的,xml文件你可以理解为是当前接口的实现类。注意点1:当前接口需要加注解@Mapper,将当前类交由spring管理。

注意点2:如果接口中的方法参数是多个(>1)的时候需要对参数加@Param进行名称的指定,这里使用注解指定的名称就是在xml中取值使用的名称而不是参数的名称,eg:String find(@Param(“aa”) int bb),如果想在xml中取到参数bb对应的值,那么就应该获取名称为aa的变量,${aa}或者#{aa}。如果方法的参数是一个或者没有,那么@Param注解可加可不加。

3.4 Service

项目中采用接口+实现类的形式:

实现类ServiceImpl:

@Service("userServiceImpl")
public class UserServiceImpl implements UserService { @Autowired
private UserDao userDao; @Autowired
private UserInfoMapper userInfoMapper; @Override
public UserInfo findByName(String name) {
return userDao.findByUserNickName(name);
} @Override
public List<UserInfo> findAll() {
return userDao.findAll();
} @Override
public UserInfo findOne(Long id) {
return userDao.findById(id).isPresent()?userDao.findById(id).get():new UserInfo();
} @Override
public UserInfo save(UserInfo UserInfo) {
return userDao.save(UserInfo);
} @Override
public UserInfo saveAndUpdate(UserInfo UserInfo) {
return userDao.saveAndFlush(UserInfo);
} @Override
public void deleteUser(long id) {
userDao.deleteById(id);
} @Override
public UserInfo findById(long id){
return userInfoMapper.selectByPrimaryKey(id);
} @Override
public int insert(UserInfo userInfo) { //用户注册的时候只有用户名和密码,所以为用户设置必填的一些字段,
//主键
userInfo.setUserId(KeyUtils.UUID());
Date nowDate = new Date();
//注册时间、创建时间
userInfo.setUserRegisterDate(nowDate);
userInfo.setCreateDate(nowDate);
//剩余登陆次数,默认5次
userInfo.setLoginNum(L_FIVE);
//性别,若没有则设置为true,true表示男,数据库对应的是1
if(StringUtils.isEmpty(userInfo.getUserSex())){
userInfo.setUserSex(true);
}
//用户姓氏取用户名的第一个字符
userInfo.setUserSurName(userInfo.getUserNickName().substring(I_ZERO, I_ONE));
userInfo.setUserName(userInfo.getUserNickName().substring(I_ONE)); //用户状态默认有效
userInfo.setStatusCd(StatusCdEnum.ACTIVE_ENUM.getCode());
//用户等级
userInfo.setUserLevel(L_ONE);
//用户密码默认采用MD5进行加密
userInfo.setPassWord(EncryptUtil.getInstance().MD5(userInfo.getPassWord())); return userInfoMapper.insert(userInfo);
} @Override
@Transactional
public UserEnum login(UserInfo userInfo) {
String userNickName = userInfo.getUserNickName();
String passWord = userInfo.getPassWord();
//用户名或密码为空,直接返回
if(StringUtils.isEmpty(userNickName) || StringUtils.isEmpty(passWord)){
return LOGIN_USER_PWD_NULL;
} //登陆的步骤:
//1、首先根据用户名查询数据库有效的数据且次数大于0的是否存在,若用户名不存在则直接返回,若用户名存在则继续下面的操作
UserInfo ui = new UserInfo();
ui.setStatusCd(StatusCdEnum.ACTIVE_ENUM.getCode());
ui.setLoginNum(L_ZERO);
ui.setUserNickName(userNickName);
List<UserInfo> userInfos = userInfoMapper.findByExample(ui);
if(CollectionUtils.isEmpty(userInfos)){
return LOGIN_USER_NULL;
}
//2、对比查询出的密码,密码不正确修改对应的数据的登陆次数
for (UserInfo info : userInfos) {
//如果密码不相等,比较次数
if(!EncryptUtil.getInstance().MD5(passWord).equals(info.getPassWord())){
//密码不相等,首先将该账号的登陆次数减一
UserInfo u = new UserInfo();
u.setUserId(info.getUserId());
u.setLoginNum(info.getLoginNum()-L_ONE);
//次数>1表示当前用户此次失败后不会被锁定
if(info.getLoginNum() > L_ONE){
int i = userInfoMapper.updateByPrimaryKeySelective(u);
if (i>I_ZERO) {
LOGIN_USER_ERROR.setNum(String.valueOf(info.getLoginNum()-L_ONE));
return LOGIN_USER_ERROR;
}
}else{
//次数<1表示此次失败后,账号将被锁定
u.setStatusCd(StatusCdEnum.FROZEN_ENUM.getCode());
u.setStatusDate(new Date());
int i = userInfoMapper.updateByPrimaryKeySelective(u);
if(i>I_ZERO){
return LOGIN_USER_NUM;
}
}
}
} //根据用户名和密码查询数据库
return UserEnum.SUCCESS;
}
}

实现类中主要对登陆和注册的相关逻辑操作,这里说下相关逻辑:

注册:界面中只输入用户名和密码,在业务层将必填的字段进行补全,密码进行加密,状态为有效,登陆次数为默认5次,注册时间为当前时间等。

登陆:根据用户名进行数据库的查询(有效的、登陆次数>1),如果查不到,报错返回,如果查到数据则判断查到的密码和输入的密码是否相等,如果相等,通过,如果不相等,则判断登陆次数是否>1,如果大于1则将剩余的次数报错(用户名或密码不正确,剩余次数*次)返回,如果<1则报错返回错误信息(该用户失败次数已达5次,请次日再试)。

接口Service:

public interface UserService {
/**
* 根据名称查询user对象
* @param name 名称
* @return user对象
*/
UserInfo findByName(String name); List<UserInfo> findAll(); UserInfo findOne(Long id); UserInfo save(UserInfo UserInfo); UserInfo saveAndUpdate(UserInfo UserInfo); void deleteUser(long id);
UserInfo findById(long id); int insert(UserInfo userInfo); UserEnum login(UserInfo userInfo);
}

3.5 Controller

这个是业务的controller控制层,也没有什么复杂的逻辑,只需要注意一点就是,如果想要返回到页面中那么controller的注解不能使用@RestController,应该使用@Controller,如果只是返回数据则无所谓。因为@RestController会将返回的结果转换为json串,所以不能返回到页面中。

@Api("用户表操作控制层")
@RestController
@RequestMapping("/user")
public class UserController { @Autowired
private UserService userService; /**
* 根据名称查询
* @param name 名称
* @return 返回User对象
*/
@GetMapping("/user/name")
@ApiOperation("根据名称查询user对象,JPA版")
public UserInfo findByName(@RequestParam("name") String name){
return userService.findByName(name);
} /**
* 查询所有的user对象
* @return user列表
*/
@GetMapping("/user/all")
@ApiOperation("查询所有的user对象,JPA版")
public List<UserInfo> findAll(){
return userService.findAll();
} /**
* 新增
*/
@PostMapping("/user")
@ApiOperation("新增user对象,JPA版")
public UserInfo save(@RequestBody UserInfo userInfo){
return userService.save(userInfo);
} /**
* 删除
*/
@DeleteMapping("/TUserInfo/{id}")
@ApiOperation("删除user对象,JPA版")
public void delete(@PathVariable("id") long id){
userService.deleteUser(id);
} /**
* 更新
*/
@PutMapping("/user")
@ApiOperation("更新user对象,JPA版")
public UserInfo update(UserInfo userInfo){
return userService.saveAndUpdate(userInfo);
} /**
* 通过Id查询
*/
@RequestMapping("/user/{id}")
@ApiOperation("查询user对象,JPA版")
public UserInfo findOne(@PathVariable("id") Long id){
return userService.findOne(id);
} /**
* 通过Id查询,MyBatis版
*/
@GetMapping("/user/mybatis/{id}")
@ApiOperation("根据Id查询UserInfo对象,Mybatis版")
public UserInfo findById(@PathVariable Long id){
return userService.findById(id);
} /**
* 新增,Mybatis版
*/
@PostMapping("/user/mybatis")
@ApiOperation("新增user对象,Mybatis版")
public int insert(@RequestBody UserInfo userInfo){
return userService.insert(userInfo);
} @PostMapping("/register")
@ApiOperation("用户注册")
public ResultDTO userRegister(UserInfoDTO userInfoDTO){
UserInfo userInfo = EntityToDTO.INIT.toUserInfo(userInfoDTO);
int result = userService.insert(userInfo);
if(result > 0){
return ResultUtils.SUCCESS();
}
return ResultUtils.ERROR();
} @PostMapping("/login")
@ApiOperation("用户登陆")
public ResultDTO userLogin(UserInfoDTO userInfoDTO){
UserInfo userInfo = EntityToDTO.INIT.toUserInfo(userInfoDTO);
UserEnum result = userService.login(userInfo);
if(result == UserEnum.SUCCESS){
return ResultUtils.SUCCESS();
}
return ResultUtils.ERROR(result.getCode(),result.getMsg(),result.getNum());
}
}

这个是视图controller,专门用户视图的跳转

@Api("用于视图转换")
@Controller
@RequestMapping("/view")
public class ViewController { /**
* 进入注册界面
*/
@GetMapping("/register")
@ApiOperation("进入用户注册")
public String enterRegister(){
System.out.println("进入注册界面...");
return "index";
} }

没有什么复杂的逻辑,这里不赘述。

3.6 html页面

html页面 中采用ajax进行登录和注册的交互,具体的页面如下:

注册和登陆的代码相差无几,这里就只贴注册的代码。

$.ajax({
type:"POST",
url:"/user/register",
data:{
"userNickName": username,
"passWord": password
},
dataType:"json",
success:function(dataX){
console.log("返回的code为:"+dataX.code);
if(dataX.code === "000000"){
$("#login-username").val(username);
$("#login-password").val(password);
//注册成功
spop({
template: '<h4 class="spop-title">注册成功</h4>即将于3秒后返回登录',
position: 'top-center',
style: 'success',
autoclose: 3000,
onOpen : function(){
var second = 2;
var showPop = setInterval(function(){
if(second == 0){
clearInterval(showPop);
}
$('.spop-body').html('<h4 class="spop-title">注册成功</h4>即将于'+second+'秒后返回登录');
second--;
},1000);
},
onClose : function(){
goto_login();
}
});
}else{
alert("注册失败:" + dataX.msg)
}
},
error:function(jqXHR){
alert("注册异常:"+ jqXHR.statusText);
}
});

页面效果如下:

SpringBoot写一个登陆注册功能,和期间走的坑

SpringBoot写一个登陆注册功能,和期间走的坑

3.7 yml文件配置

由于springBoot的结构是在resources目录下有专门的templates文件夹和static文件夹,

templates用于放置一些html页面,static放置一些静态资源,由于SpringBoot会默认访问resources下的这两个文件夹下的html和js、css…文件,关于html和静态资源需要在yml文件中进行路径的配置,否则会访问不到静态资源,这里本人就踩了不少坑。

spring:
# 配置这个表示访问templates页面路径的前缀
thymeleaf:
prefix: classpath:/templates/
# 访问静态资源的路径,可以是多个,表示请求的静态资源会查找的目录
resources:
static-locations:
classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
# 访问静态资源的请求方式,就是html请求静态资源的时候需要以static开头
mvc:
static-path-pattern: /static/**

3.8 POM文件依赖

这里引用的依赖大多都是直接从maven仓库中找的稳定的新的,需要的可自行去搜索查找,点我

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description> <properties>
<java.version>1.8</java.version>
<swagger.version>2.9.2</swagger.version>
<mybatis.version>2.0.0</mybatis.version>
<fastjson.version>1.2.57</fastjson.version>
<mapstruct.version>1.2.0.Final</mapstruct.version>
</properties> <dependencies>
<!-- springWeb端的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring JPA的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- mysql数据库的依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- redis依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- swagger依赖 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- mybatis 依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- fastjson json转换工具类 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- mapstruct 转换类依赖 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
<scope>provided</scope>
</dependency>
<!-- springBoot的静态资源依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 实现springBoot的热部署依赖 -->
<!-- 实现热部署参考https://blog.****.net/qq_41700133/article/details/82224390 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>true</scope>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build> <!-- <finalName>xml</finalName>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>*</include>
<include>**/*</include>
</includes>
&lt;!&ndash; 去除资源文件 &ndash;&gt;
<excludes>
<exclude>/generatorConfig.xml</exclude>
</excludes>
</resource>
</resources>--> <plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin> <!-- <plugin>
&lt;!&ndash; 指定maven编译的jdk版本,如果不指定,maven3默认用jdk 1.5 maven2默认用jdk1.3 &ndash;&gt;
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
&lt;!&ndash; 一般而言,target与source是保持一致的,但是,有时候为了让程序能在其他版本的jdk中运行(对于低版本目标jdk,源代码中不能使用低版本jdk中不支持的语法),会存在target不同于source的情况 &ndash;&gt;
<source>${java.version}</source> &lt;!&ndash; 源代码使用的JDK版本 &ndash;&gt;
<target>${java.version}</target> &lt;!&ndash; 需要生成的目标class文件的编译版本 &ndash;&gt;
<encoding>${project.build.sourceEncoding}}</encoding>&lt;!&ndash; 字符集编码 &ndash;&gt;
&lt;!&ndash;如果没有这个属性 springBoot 自动配置不会生效&ndash;&gt;
<fork>true</fork>
</configuration>
</plugin>--> <!-- maven指定不编译的java文件 -->
<!-- <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<excludes>
<exclude>resources/generatorConfig.xml</exclude>
</excludes>
</configuration>
</plugin>--> </plugins>
</build> </project>
**至此从前端到后端使用springboot开发的登陆注册就完成了。**

4. 踩坑秘籍

4.1 xml文件返回结果是list的size为1,但是结果为null

如果数据库表字段和实体的类型匹配不到,在结果返回的时候使用resultType是不能将结果正常的返回出来的,我遇到的情况是查询出数据条数为1,结果的list的size的确是1,但是里面却是null,这个就是因为字段类型不匹配,导致的数据没有被填充到指定的实体中,这个时候应该使用resultMap对表字段和实体字段进行指定。这样就能正常的返回数据了。

4.2 Controller返回到html

如果想要返回到页面中那么controller的注解不能使用@RestController,应该使用@Controller,如果只是返回数据则无所谓。因为@RestController会将返回的结果转换为json串,所以不能返回到页面中。

4.3 关于静态资源的配置

由于springBoot的结构是在resources目录下有专门的templates文件夹和static文件夹,

templates用于放置一些html页面,static放置一些静态资源,由于SpringBoot会默认访问resources下的这两个文件夹下的html和js、css…文件,关于html和静态资源需要在yml文件中进行路径的配置,否则会访问不到静态资源,这里本人就踩了不少坑。

spring:
# 配置这个表示访问templates页面路径的前缀
thymeleaf:
prefix: classpath:/templates/
# 访问静态资源的路径,可以是多个,表示请求的静态资源会查找的目录
resources:
static-locations:
classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
# 访问静态资源的请求方式,就是html请求静态资源的时候需要以static开头
mvc:
static-path-pattern: /static/**

4.4 Swagger 配置

swagger需要在pom文件先添加依赖,具体依赖看上面的介绍,然后自定义一个swagger配置类,就可以在Controller中使用了,配置类参考:

@Configuration
@EnableSwagger2
public class SwaggerConfig { @Bean
public Docket buildDocket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(buildApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
.paths(PathSelectors.any())
.build();
} public ApiInfo buildApiInfo(){
return new ApiInfoBuilder()
.title("SpringBoot配置Swagger文档API")
.description("简单优雅的RestFun风格")
.version("1.0")
.build();
}
}

4.5 mapstruct的配置和使用

mapstruct的配置我踩坑比较多,最初直接在在maven仓库中搜索找到了依赖直接放进去,写了接口类,发现编译后并不能自动生成实现类。

我在maven仓库下载的是core

SpringBoot写一个登陆注册功能,和期间走的坑

代码执行过程中一直报错,后面在网上查找答案才发现,我引用错依赖了,应该引用下面两个:

SpringBoot写一个登陆注册功能,和期间走的坑

添加完正确的引用后,写一个工具接口类就可以了:

SpringBoot写一个登陆注册功能,和期间走的坑

代码下载地址

如果文章能够看懂,请自行编码,如实在完成不了再下载源码,编码注重的是自己敲代码的过程。下载地址点我

至此文章已全部结束,文章用来记录自己踩过的坑,同时希望能帮助一些人,文章篇幅过长,有问题希望大家指正。我们一起成长!!