文章目录
1.理解session2.理解ThreadLocal2.1 理解多线程2.2 理解lambda表达式2.3 ThreadLocal
3.基于session登录流程图4.具体登录的代码实现4.1短信发送功能4.2 短信验证码登录注册功能4.登录校验功能4.1 配置登录拦截器LoginInterceptor4.1.1 ThrealLocal类实现
4.2登录拦截器加入到MvcConfig中4.3 用户登录访问
1.理解session
用户第一次在浏览器输入正确的账号密码进行http请求时候,后端会根据他的信息设置一个该账户唯一的session并保存在后端,并设置有效期,最后返回保存到浏览器cookie中。下一次请求需要浏览器携带session到后端,校验通过,用户便可以无需登录访问,否则直接被拦截。
2.理解ThreadLocal
2.1 理解多线程
实现多线程有两种方式:(.start() 方法启动) 1.继承Thread类,在类的内部写任务执行(也就是run方法) 2.实现Runnable接口,接口内部重写run方法,在把这个实现类传递到Thread线程内部即可 总结:推荐方式2,解耦合
2.2 理解lambda表达式
1.注意lambda表示式必须和函数式接口结合使用 2.函数式接口,指的是接口中只有一个抽象方法
2.3 ThreadLocal
线程内部存放该线程对应的自己数据,线程之间相互隔离,主要用在多线程并发的场景下
3.基于session登录流程图
1.为什么验证码是存储在session? 因为验证码是临时的,存储在session,前端请求的时候后端才能正确比对他填写的是否正确 2.后端没有显示设置session保存到浏览器?为什么前端请求后这个session会自动保存到浏览器的cookie里面? 用户登录成功,服务器会创建一个Session,生成唯一的JSESSIONID,JSESSIONID会通过Set-Cookie HTTP响应头返回给浏览器。这个过程是web容器(tomcat)自动完成,无需开发者介入
4.具体登录的代码实现
4.1短信发送功能
@Override
public Result sendCode(String phone, HttpSession session) {
// 1.校验手机号
if (RegexUtils.isPhoneInvalid(phone)){
// 2.如果不符合,返回错误信息
return Result.fail("手机号格式错误");
}
// 3.符合,生成验证码
String code = RandomUtil.randomNumbers(6);
// 4.保存验证码到session中(存储再服务器内存中)
session.setAttribute("code", code);
// 5.发送验证码
log.debug("发送短信验证码成功,验证码:{}", code);
return Result.ok();
}
4.2 短信验证码登录注册功能
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
// 1.校验手机号
String phone = loginForm.getPhone();
if (RegexUtils.isPhoneInvalid(phone)){
// 2.如果不符合,返回错误信息
return Result.fail("手机号格式错误");
}
// 2.校验验证码
String cacheCode = (String)session.getAttribute("code");
String code = loginForm.getCode();
if (cacheCode == null || !cacheCode.equals(code)){
return Result.fail("验证码不一致");
}
// 3.根据手机号码查询用户信息 select * from tb_user where phone = ? 实现了mybatisplus接口ServiceImpl
User user = query().eq("phone", phone).one(); // query是mybatisplus的功能
// 4.判断用户是否存在
if (user == null) {
// 5.用户不存在,创建新用户并保存
user = createUserWithPhone(phone);
}
//7.保存用户信息到session中
session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));
return Result.ok();
}
这里可能存在一个问题,使用自己手机号接收验证码,别人手机号登录问题
4.登录校验功能
4.1 配置登录拦截器LoginInterceptor
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.getAttribute("user");
// 3.判断用户是否存在
if (user == null) {
response.setStatus(401);
return false;
}
// 5.存在,保存用户信息到ThreadLocal
UserHolder.saveUser((UserDTO)user);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 移除用户
UserHolder.removeUser();
}
}
4.1.1 ThrealLocal类实现
public class UserHolder {
private static final ThreadLocal
public static void saveUser(UserDTO user) {
tl.set(user);
}
public static UserDTO getUser() {
return tl.get();
}
public static void removeUser() {
tl.remove();
}
}
4.2登录拦截器加入到MvcConfig中
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns(
"/user/code",
"/user/login",
"/blog/hot",
"/shop/**",
"/shop-type/**",
"/upload/**",
"/voucher/**"
);
}
}
4.3 用户登录访问
@GetMapping("/me")
public Result me(){
// 获取当前登录的用户并返回
UserDTO user = UserHolder.getUser();
return Result.ok(user);
}
总结:用户登录访问的页面需要拦截器拦截,拦截判断成功之后,才会请求访问对应的Controller