首先,在数据库中查询这条用户记录,如果不存在这条记录则表示身份验证失败,登录流程终止;如果存在这条记录,则表示身份验证成功,接下来则需要进行登录状态的存储和验证了,用户登录成功后我们将用户信息放到 session 对象中,之后再实现一个拦截器,在访问项目时判断 session 中是否有用户信息,有则放行请求,没有就跳转到登录页面。
2.登陆页面制作 2.1 Controller控制跳转在 BBSUserController.java类中新增跳转功能。该方法用于处理 /login 请求,是登录页面的跳转处理方法,请求方法为 GET,在发起请求后会分别跳转到 templates 模板目录中 user 目录下的 login.html 中。
@GetMapping({"/login", "/login.html"})
public String loginPage() {
return "user/login";
}
2.2 创建登陆页面
登录页面与注册页面非常类似,只是二者的功能有些区别而已。
3. 完成登陆功能 3.1 BBSUserController层代码
- 登入
- 注册
这里负责接收前端传来的用户登录相关参数,包括登录邮箱、密码等;接口的映射地址为 /login,请求方法为 POST。首先对参数进行校验,之后调用 bbsUserService 业务层代码查询用户输入的登录信息是否正确,最后根据业务层的结果来返回对应的 Result 对象。
@PostMapping("/login")
@ResponseBody
public Result login(@RequestParam("loginName") String loginName,
@RequestParam("verifyCode") String verifyCode,
@RequestParam("password") String password,
HttpSession httpSession) {
if (!StringUtils.hasLength(loginName)) {
return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_NAME_NULL.getResult());
}
if (!PatternUtil.isEmail(loginName)) {
return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_NAME_NOT_EMAIL.getResult());
}
if (!StringUtils.hasLength(password)) {
return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_PASSWORD_NULL.getResult());
}
if (!StringUtils.hasLength(verifyCode)) {
return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_VERIFY_CODE_NULL.getResult());
}
String kaptchaCode = httpSession.getAttribute(Constants.VERIFY_CODE_KEY) + "";
if (!StringUtils.hasLength(kaptchaCode) || !verifyCode.equals(kaptchaCode)) {
return ResultGenerator.genFailResult(ServiceResultEnum.LOGIN_VERIFY_CODE_ERROR.getResult());
}
String loginResult = bbsUserService.login(loginName, MD5Util.MD5Encode(password, "UTF-8"), httpSession);
//登录成功
if (ServiceResultEnum.SUCCESS.getResult().equals(loginResult)) {
httpSession.removeAttribute(Constants.VERIFY_CODE_KEY);//删除session中的验证码
return ResultGenerator.genSuccessResult();
}
//登录失败
return ResultGenerator.genFailResult(loginResult);
}
3.2 BBSUserServiceImpl业务层代码
首先根据登录名和密码查询数据库中是否有对应的用户记录,不存在则返回错误信息,如果存在则将用户信息保存到 session 对象中,最后返回登录成功的业务信息。
String login(String loginName, String passwordMD5, HttpSession httpSession);
@Override
public String login(String loginName, String passwordMD5, HttpSession httpSession) {
BBSUser user = bbsUserMapper.selectByLoginNameAndPasswd(loginName, passwordMD5);
if (user != null && httpSession != null) {
// 将用户信息存入session中
httpSession.setAttribute(Constants.USER_SESSION_KEY, user);
//修改最近登录时间
user.setLastLoginTime(new Date());
bbsUserMapper.updateByPrimaryKeySelective(user);
return ServiceResultEnum.SUCCESS.getResult();
}
return ServiceResultEnum.LOGIN_ERROR.getResult();
}
3.3 BBSUserMapper数据持久层
主要的 SQL 方法为 selectByLoginNameAndPasswd(),根据用户名和密码查询用户记录信息。接着updateByPrimaryKeySelective()修改登录时间。
BBSUser selectByLoginNameAndPasswd(@Param("loginName") String loginName, @Param("password") String password);
int updateByPrimaryKeySelective(BBSUser record);
update tb_bbs_user
login_name = #{loginName,jdbcType=VARCHAR},
password_md5 = #{passwordMd5,jdbcType=VARCHAR},
nick_name = #{nickName,jdbcType=VARCHAR},
head_img_url = #{headImgUrl,jdbcType=VARCHAR},
gender = #{gender,jdbcType=VARCHAR},
location = #{location,jdbcType=VARCHAR},
introduce = #{introduce,jdbcType=VARCHAR},
user_status = #{userStatus,jdbcType=TINYINT},
last_login_time = #{lastLoginTime,jdbcType=TIMESTAMP},
create_time = #{createTime,jdbcType=TIMESTAMP},
where user_id = #{userId,jdbcType=BIGINT}
3.4 前后端交互
layui.use(['layer', 'jquery'], function () {
var layer = layui.layer;
var $ = layui.$;
window.login = function () {
var loginName = $("#loginName").val();
if (!validEmail(loginName)) {
layer.alert('请输入正确的登录名!', {title: '提醒', skin: 'layui-layer-molv', icon: 2});
return false;
}
var password = $("#password").val();
if (!validPassword(password)) {
layer.alert('请输入正确的密码格式!', {title: '提醒', skin: 'layui-layer-molv', icon: 2});
return false;
}
var verifyCode = $("#verifyCode").val();
if (!validLength(verifyCode, 5)) {
layer.alert('请输入正确的验证码!', {title: '提醒', skin: 'layui-layer-molv', icon: 2});
return false;
}
var params = $("#loginForm").serialize();
var url = '/login';
$.ajax({
type: 'POST',//方法类型
url: url,
data: params,
success: function (result) {
if (result.resultCode == 200) {
window.location.href = '/index';
} else {
layer.msg(result.message);
}
;
},
error: function () {
layer.alert('操作失败!', {title: '提醒', skin: 'layui-layer-molv', icon: 2});
}
});
}
});
3.5 创建一个index.html首页 3.5.1 创建一个简单首页1.首先是用 jQuery 语法将用户输入的字段获取到
2.使用正则表达式验证用户的输入字段是否符合规范
3.封装数据并向登录接口发送 Ajax 请求
4.请求成功后会跳转至首页 /index,如果想跳转到其他页面可以在这里更改路径
5.请求失败则提醒对应的错误信息
这里只需要创建一个简单的页面验证登陆功能即可。
Document
登陆成功!!!
3.5.2 Controller控制跳转
@Controller
public class IndexController {
@GetMapping("/index")
public String indexPage(){
return "index";
}
}
4.验证登陆功能
启动项目验证登陆功能,输入帐号 123456@qq.com,密码 123456,再输入正确的验证码,之后点击 “登录” 按钮就可以完成登录流程。效果如下展示:
登录的目的是为了能够访问一些需要身份信息的页面,比如个人信息页面、添加文章页面,如果没有正确登录的话,是不允许正常的访问页面的,而是跳转至登录页面让用户完成登录才能够继续操作。
拦截器实现
定义一个 Interceptor 非常简单,方式也有几种,这里列举两种简单的实现:
- 新建类实现 Spring 的 HandlerInterceptor 接口
- 新建类继承实现了 HandlerInterceptor 接口的实现类,例如已经提供的实现了 HandlerInterceptor 接口的抽象类 HandlerInterceptorAdapter
HandlerInterceptor 方法介绍
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
- preHandle:在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理。
- postHandle:在业务处理器处理请求执行完成后,生成视图之前执行。
- afterCompletion:在 DispatcherServlet 完全处理完请求后被调用,可用于清理资源等,返回处理(已经渲染了页面)。
新建 interceptor 包,在包中新建 MyBBSLoginIntercepto 类,该类需要实现 HandlerInterceptor 接口。
@Component
public class MyBBSLoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
if (null == request.getSession().getAttribute(Constants.USER_SESSION_KEY)) {
response.sendRedirect(request.getContextPath() + "/login");
return false;
} else {
return true;
}
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
5.2 配置拦截器
在实现拦截器的相关方法之后,我们需要对该拦截器进行配置才能使其生效。
强调一下: 在 Spring Boot 1.x 版本中通常会继承 WebMvcConfigurerAdapter 类,但是在 Spring Boot 2.x 版本中,WebMvcConfigurerAdapter 被弃用,虽然继承 WebMvcConfigurerAdapter 这个类比较便利,但在 Spring 5.0 里面已经被弃用了,官方文档也说了,WebMvcConfigurer 接口现在已经有了默认的空白方法,所以在 Spring Boot 2.x 版本下更好的做法还是实现 WebMvcConfigurer 接口。
新建 config 包,之后新建 MyBBSWebMvcConfigurer.java 类并实现 WebMvcConfigurer 接口。
@Configuration
public class MyBBSWebMvcConfigurer implements WebMvcConfigurer {
@Autowired
private MyBBSLoginInterceptor myBBSLoginInterceptor;
public void addInterceptors(InterceptorRegistry registry) {
// 登陆拦截
registry.addInterceptor(myBBSLoginInterceptor)
.excludePathPatterns("/register")
.excludePathPatterns("/login")
.addPathPatterns("/index");
}
}
其中 excludePathPatterns() 方法是设置请求不被拦截器拦截,而 addPathPatterns() 方法则是设置拦截器对这些请求生效,因此上述代码中,对于注册请求和登录请求是放行的,也就是访问这两个地址是不会进行 session 中是否有登录用户对象的验证,而对于 /index 请求则会判断是否已经成功登录。
6.验证拦截器在地址栏输入index会跳转到登陆界面。
至此登陆功能和登陆拦截器功能也实现完成了。
项目源码下载地址:项目源码下载



