用户登录是网站基本上最常见的功能了。当然,使用Spring Boot实现用户登录也不是难事,今天我将分享,从零开始制作一个用户登录功能的过程。
当然,大家还是需要掌握Spring Boot基本使用以及MyBatis的配置(SSM基本操作),再来看这篇文章比较好。
在最开头我们先约定:主类所在的项目包路径: com.example.userlogin, 后面其余新建的软件包都在这个包路径之下。
文章最末尾我也会给出示例的仓库地址。
1,基本知识-用户登录的实现原理
对于用户登录,我们一定都听说过
cookie这个词。其实,
cookie是网络编程中使用最广泛的技术之一,它用于储存用户的登录信息。它储存在用户本地,每次随着网络请求发送给服务端,服务端就用这个判断用户是否登录。可以看看这个图:
可见,用户未登录之前,http请求是不带cookie的,登录后,客户端会将登录信息放在请求中给服务端,服务端进行验证,登录成功后,服务端会把用户信息放在cookie里面,随着响应返回给客户端,客户端就会储存这个cookie。下次再访问网站,cookie会和客户端的请求一起发给服务端,服务端验证信息正确,判断这个用户是登录状态。
cookie里面也是以
key-value形式储存数据,并且cookie有它自己的属性例如生命周期、生效域名等等。
但是实际上,cookie里面由于存放着用户信息,很容易存在安全隐患,cookie可以被拦截甚至伪造。
因此现在网站登录都使用
session机制,它和
cookie机制最大的区别就是
用户信息不再放在客户端而是服务端,交互过程如图:
可见
session机制就是
以cookie作为载体,cookie里面只储存一个session id,与服务端通信。每个客户端每一次登录请求都会和服务端会生成唯一的session id,相当于客户端每次只要告诉服务端session id,服务端就可以找到相对应的客户端数据。
因此下面示例我们都使用
session机制进行。使用Spring Boot可以很轻松的实现
session读写。
2,开始-进行配置
我们需要配置如下依赖:
-
jackson-annotationsjson配置注释
-
codec编码加密,用于加密储存用户密码
-
common-lang3实用工具集
-
Spring Web最基本的Spring Boot网络支持
-
Validation用于信息校验
-
Spring SessionSpring Boot的session操作支持
-
MyBatis大名鼎鼎的数据库持久框架,操作数据库
-
Lombok代码简化,不多说
-
Spring Test内置测试功能,不多说
依赖可以根据自己实际情况进行增删,这里我主要用这些,大多数在创建Spring Boot工程时也可以进行选择。
然后配置项目配置文件
application.properties
对于Mybatis的Mapper XML文件,默认位于
src/main/resources/com/{xxx}/{xxx}/dao/之下,即和你的dao包位置对应。例如我们项目的Mapper类一般会放在
com.example.userlogin.dao下,那么Spring Boot就默认去这个地方扫描Mapper XML:
src/main/resources/com/example/userlogin/dao,目录需手动创建。当然我们也可以进行指定:
这里我们的项目就不配置MyBatis的XML位置了,就使用默认位置。配置全部根据自己实际情况进行填写。
3,封装一个请求结果类
Result
为了方便起见,我们通常封装一个结果类,里面主要是请求结果代码、消息、是否操作成功和数据体,可以根据自己需要进行修改。
新建软件包
model,并在其中新建
Result内容如下:
一般都会返回给前端这个结果对象,更加便捷的传递是否成功以及操作消息等等。
注意前后端传递交互的数据都需要 实现序列化接口并且要 有无参构造器,这里使用了Lombok的注解,下面也一样。
4,建立用户类,并初始化数据库表
创建软件包
dataobject,并在其中建立用户类
User,实际根据业务需要不同,用户类可能有很多属性,这里我们只建立最简单的如下:
我们设定了用户的最基本属性,并对其设定了校验规则,密码为敏感信息,因此我们使用Jackson注解设定密码字段为不允许序列化(不会传给前端)。
创建了用户类,我们数据库表也需要对应起来,初始化一个用户表,我这里sql如下:
连接MySQL并use相应数据库,将这个sql文件使用source命令执行即可。
每个对象都有主键id,且一般设为自增无符号整数,这样有最高的数据库读写效率。密码一般使用MD5加密储存,因此固定32位长度。一般每个数据库表都有创建时间gmt_created和修改时间gmt_modified字段,这里简单起见省略。
5,创建数据服务层-DAO并配置Mapper XML
DAO层主要是Java的对于数据库操作的接口和实现类。MyBatis的强大之处,就是只需定义接口,就可以实现操作数据库。
创建软件包
dao,新建接口
UserDAO:
根据实际需要定义数据库增删改查方法,这里就定义这些。注意这里接口上面要打上
@Mapper注解表示它是个数据持久层接口。且一般来说,
增删改方法的返回值都是int,表示操作成功记录条数,
查方法一般是返回相应对象或者对象的List。
然后编写Mapper XML文件,我们在项目文件夹下的
src/main/resources目录下创建多级目录:
com/example/userlogin/dao,在这个目录下存放XML文件。
创建
UserDAO.xml内容如下:
这样,数据库操作层就完成了!
.一般约定某一个对象(xxx)的数据库操作层接口一般命名为xxxDAO(有的企业也命名为xxxMapper),一个xxxDAO接口对应一个xxxDAO.xml文件。
6,创建用户服务层
现在就要进行正式的服务层逻辑了,完成我们的主要功能:用户注册、登录、信息修改。
其实,用户注册就是前端发送用户注册信息(封装为
User对象),后端检验然后往数据库
增加一条用户记录的过程;登录也是前端发送用户登录信息,同样封装为
User对象,后端根据这个对象的
username字段
从数据库取出用户、进行比对最后设定session的过程;修改用户也是前端发送修改后的用户信息的
User对象,后端进行比对,然后
修改数据库相应记录的过程。
先新建包
service,在其中添加用户服务接口
UserService:
然后再在包
service下建立包
impl,然后在里面新建用户服务实现类
UserServiceImpl:
需要注意的是服务接口需要有
@Service注解,接口实现类要有
@Component注解,还自动注入了DAO的实例进行数据库操作。
7,创建用户登录API
服务层写完了,现在就是前后端交互的桥梁需要打通了-编写API,这样前端才能发送请求调用我们后端的服务。
我们的API要实现用户 登录、 注册、 判断用户是否登录、 修改用户信息、 用户登出这几个功能。
新建包
api,然后在里面新建类
UserAPI:
因为是API,所以使用
@RestController标注类,可见注册登录都是通过前端POST请求后端接收,且在各个方法中加入参数
HttpServletRequest request,就可以通过
request对象对session进行读写。每一个不同的请求都会有一个唯一的
session,每个
session中的信息都是
key-value形式储存,这里我们只在session里面储存用户信息,因此我们把用户信息的key设为一个固定的名字
userInfo,通过建立个常量
SESSION_NAME。
每个不同机器的请求都会生成独一无二的session,上面这段代码操作,我们可以理解为:登录/注册用户后,取得了用户信息,我们将每个不同机器的用户信息储存在了与它们相对应的session里面,并在其中设定用户信息的
key为
userInfo。
通过
HttpServletRequest的
getSession方法,即可获取这个请求的session对象,为
HttpSession类型,其中
setAttribute方法用于设定session中的键值对,
getAttribute方法用于获取session中信息。
8,配置cookie属性
其实到上面第7步,我们的功能基本上完整了,但是还有一些重要配置需要进行。
我们还要开启session功能,并配置cookie属性例如过期时间等等。
新建包
config,在其中建立配置类
SessionConfig:
注意配置类打上
@Configuration注解,
@EnableSpringHttpSession开启session。
9,配置拦截器
虽然我们有判断用户登录的API,但是如果我们页面很多,每一个都要判断登录,就会很麻烦。通过拦截器即可对指定的路径设定拦截点。
继续在
config包下创建拦截器类
UserInterceptor:
可见拦截器有三个方法,分别对应三个切入点。一般我们只需要修改在Controller执行之前的那个,它可以像API一样操作session,返回true时表示允许继续访问这个Controller,否则终止访问。通过
HttpServletResponse的
sendRedirect方法可以发送重定向。
拦截器类写好了,接下来就是注册拦截器了。在
config类中创建配置类
InterceptorRegister:
在
addInterceptors方法里面,我们进行拦截器注册,并配置拦截地址。上述例子只添加拦截
/update这个路径,其余不拦截。
还可以设定拦截全部,只排除
/login,如下写
addInterceptors方法:
在此,一个比较简易但完整的用户注册登录就做完了!
10,总结
用户登录注册看起来要写的东西很多,但实际上流程很清晰,也不难理解。
我们发现,DAO层就是单纯操作数据库,返回用户对象;Service层基本上就是用于验证信息正确性,返回封装的
Result对象;Controller层进一步设定session,也是返回封装
Result对象。每个不同的部分各司其职,完成了我们整个业务逻辑。