电商系统:用户模块之登录功能
一、功能描述
用户输入账号密码进行登录,可以通过cookie、session保存当前用户信息

二、登录功能
1、Controller层
专门定义一个form包下的UserLoginForm类用于接收请求参数,通过@valid进行参数验证,确保参数不能为空,登录成功后将信息保存到session中
@PostMapping("/user/login")
public ResponseVO login(@Valid @RequestBody UserLoginForm userLoginForm, BindingResult bindingResult, HttpSession session){
if(bindingResult.hasErrors()){
log.info("登录提交的参数有误,{}", bindingResult.getFieldError().getDefaultMessage());
return ResponseVO.error(ResponseEnum.PARAM_ERROR, bindingResult);
}
ResponseVO<User> responseVO = userService.login(userLoginForm.getUsername(), userLoginForm.getPassword());
//设置session
session.setAttribute(MallConst.CURRENT_USER, responseVO.getData());
return responseVO;
}
2、Service层
判断用户名、密码是否正确,密码置空
@Override
public ResponseVO<User> login(String username, String password) {
User user = userMapper.selectByUsername(username);
if(user == null){
//用户不存在
return ResponseVO.error(ResponseEnum.USERNAME_OR_PASSWORD_ERROR);
}
if(!user.getPassword().equalsIgnoreCase(DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8)))){
//密码错误
return ResponseVO.error(ResponseEnum.USERNAME_OR_PASSWORD_ERROR);
}
user.setPassword("");
return ResponseVO.success(user);
}
3、Dao层
自定义了根据用户名查找用户sql语句
<select id="selectByUsername" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from mall_user
where username = #{username,jdbcType=VARCHAR}
</select>
三、Cookie与Session
1、客户端向服务端发送登录请求
2、服务端在登录controller中,创建了一个session,将用户信息保存在了session中,同时自动将sessionId放入到了cookie中
session.setAttribute(MallConst.CURRENT_USER, responseVO.getData());
3、客户端在收到响应后,自身cookie中保存有sessionId

4、客户端向服务端发送请求,cookie中携带着sessionId
5、服务端在根据sessionId查找session,并获取到用户信息
@GetMapping("/user")
public ResponseVO<User> userInfo(HttpSession session){
User user = (User)session.getAttribute(MallConst.CURRENT_USER);
if(user == null){
return ResponseVO.error(ResponseEnum.NEED_LOGIN);
}
return ResponseVO.success(user);
}
6、session有存活时间,可以自行配置
7、跨域问题
127.0.0.1与localhost发起请求,cookie也是不同的,其Domain不同

四、退出登录
去除session
session.removeAttribute(MallConst.CURRENT_USER);
@GetMapping("/user/logout")
public ResponseVO<User> logout(HttpSession session){
User user = (User)session.getAttribute(MallConst.CURRENT_USER);
if(user == null){
return ResponseVO.error(ResponseEnum.NEED_LOGIN);
}
session.removeAttribute(MallConst.CURRENT_USER);
return ResponseVO.success();
}
五、拦截器-统一判断登录状态
有两种方式
Interceptor,基于url进行拦截,本次采用该方式
AOP,基于包名进行拦截
1、定义一个拦截器配置类,加上@Configuration注解,实现WebMvcConfigurer接口,添加拦截器,添加拦截路径,添加排除路径
@Configuration
public class interceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserLoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/user/login", "/user/register");
}
}
2、定义一个拦截器类
实现HandlerIntercepto接口,返回false是拦截,返回true是继续
@Slf4j
public class UserLoginInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("preHandle...");
User user = (User)request.getSession().getAttribute(MallConst.CURRENT_USER);
if(user == null){
return false;
}
return true;
}
}