黑马点评2:基于Session实现登录
1. 业务流程

2. 发送短信验证码


src/main/java/com/hmdp/controller/UserController.java
/**
* 发送手机验证码
*/
@PostMapping("code")
public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {
// 发送短信验证码并保存验证码
return userService.sendCode(phone, session);//需要写的,alt+enter到IUserService.java
}
src/main/java/com/hmdp/service/IUserService.java
public interface IUserService extends IService<User> {//ctrl+b到UserServiceImpl.java
Result sendCode(String phone, HttpSession session);//需要写的
}
src/main/java/com/hmdp/service/impl/UserServiceImpl.java
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Override
public Result sendCode(String phone, HttpSession session) {//Result:自定义的类,封装方法的返回结果
// 1.校验手机号
if(RegexUtils.isPhoneInvalid(phone)){//RegexUtils.isPhoneInvalid(phone):调用工具类方法(自定义的),检查手机号格式是否有效。
// 2.如果不符合,返回错误信息
return Result.fail("手机号格式错误!");
}
// 3.符合,生成验证码
String code = RandomUtil.randomNumbers(6);//RandomUtil.randomNumbers(6):调用工具类方法(官方),生成一个6位的随机数字字符串,作为验证码。
// 4.保存验证码到 session
session.setAttribute("code",code);//HttpSession 对象用于在服务器端存储与特定用户会话相关的数据
// 5.发送验证码(模拟发送),在后台查看
log.debug("发送短信验证码成功,验证码:{}",code);
// 返回ok
return Result.ok();
}
3. 登录功能

src/main/java/com/hmdp/controller/UserController.java
/**
* 登录功能
* @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码
*/
@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){
// 实现登录功能
return userService.login(loginForm, session);
}
src/main/java/com/hmdp/service/IUserService.java
public interface IUserService extends IService<User> {
Result sendCode(String phone, HttpSession session);
Result login(LoginFormDTO loginForm, HttpSession session);
}
src/main/java/com/hmdp/service/impl/UserServiceImpl.java
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
// 1.校验手机号(每个请求要做独立的校验)
String phone = loginForm.getPhone();
if(RegexUtils.isPhoneInvalid(phone)){
return Result.fail("手机号格式错误!");
}
// 2. 校验验证码
Object cacheCode = session.getAttribute("code");
String code = loginForm.getCode();
if( cacheCode == null || !cacheCode.toString().equals(code)){
// 3. 不一致,报错,反向校验,避免if的嵌套越来越深
return Result.fail("验证码错误");
}
// 4.一致,根据手机号查询用户 select * from tb_user where phone = ?
User user = query().eq("phone", phone).one();
// 5.判断用户是否存在
if(user == null){
// 6.不存在,创建新用户并保存
user = createUserWithPhone(phone);
}
// 7.保存用户信息到 session中
session.setAttribute("user",user);
return null;
}
private User createUserWithPhone(String phone) {
// 1.创建用户
User user = new User();
user.setPhone(phone);
user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));//USER_NICK_NAME_PREFIX为了统一一点,是自定义的常量
// 2.保存用户
save(user);
return user;
}
这里代码改了好像和前端不匹配,所以还是用回了拿到的原版代码,此时正确的效果应该是能登录创建出新用户,但是还是保留在登录界面。
4. 登录验证功能

拦截器
src/main/java/com/hmdp/utils/LoginInterceptor.java
public class LoginInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 获取session
HttpSession session = request.getSession();
// 2. 获取session中的用户
Object user = session.setAttribute("user");
// 3. 判断用户是否存在
if(user == null){
// 4. 不存在,拦截,返回401状态码(未授权的意思)
response.setStatus(401);
return false;
}
// 5. 存在,保存用户信息到TreadLocal
UserHolder.saveUser((User) user);
// 6. 放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//移除用户
UserHolder.removeUser();
}
}
src/main/java/com/hmdp/config/MvcConfig.java
@Override
public void addInterceptors(InterceptorRegistry registry) {//添加拦截器
// 登录拦截器
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns(//排除不需要拦截的路径
"/shop/**",
"/voucher/**",
"/shop-type/**",
"/upload/**",
"/blog/hot",
"/user/code",
"/user/login"
);
}
}
src/main/java/com/hmdp/controller/UserController.java
@GetMapping("/me")
public Result me(){
// 获取当前登录的用户并返回
User user = UserHolder.getUser();
return Result.ok(user);
}
隐藏用户敏感信息
src/main/java/com/hmdp/service/impl/UserServiceImpl.java
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
...
// 7.保存用户信息到 session中
session.setAttribute("user",BeanUtil.copyProperties(user, UserDTO.class));
return null;
}
src/main/java/com/hmdp/utils/LoginInterceptor.java
public class LoginInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
...
// 5. 存在,保存用户信息到TreadLocal
UserHolder.saveUser((UserDTO) user);
// 6. 放行
return true;
}
}
src/main/java/com/hmdp/utils/UserHolder.java
public class UserHolder {
private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();
public static void saveUser(UserDTO user){
tl.set(user);
}
public static UserDTO getUser(){
return tl.get();
}
public static void removeUser(){
tl.remove();
}
}
5. 集群的session共享问题

Redis替代

浙公网安备 33010602011771号