①实现基于session的登录流程:发送验证码、登录注册、校验登陆状态

①实现基于session的登录流程:发送验证码、登录注册、校验登陆状态

个人简介:Java领域优质创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~

个人主页:.29.的博客

学习社区:进去逛一逛~

实现基于session的登录流程:发送验证码、登录注册、校验登陆状态🚀流程介绍登录流程:

①验证码发送发送验证码:

用户输入手机号,点击发送按钮进行手机号提交,程序会校验手机号是否合法,不合法时要求用户重新输入手机号,合法则在后台生成对应的验证码并保存至session,之后通过短信方式将验证码发送给用户。

什么是HttpSession?

HttpSession是Java Web中的一个接口,它提供了一种在服务器端存储和检索用户相关信息的机制。当用户第一次访问Web应用程序时,服务器会为该用户创建一个唯一的session ID,并将该ID存储在一个名为JSESSIONID的cookie中,然后将该ID与一个新的HttpSession对象相关联。在用户与Web应用程序交互期间,可以使用HttpSession对象来存储和检索与该用户相关的信息。当用户关闭浏览器或超过session超时时间时,session对象将被销毁。

以下是获取和使用HttpSession对象的常用方法:

1.获取HttpSession对象:

代码语言:javascript代码运行次数:0运行复制 HttpSession session = request.getSession();2.向session中存储数据:

代码语言:javascript代码运行次数:0运行复制 session.setAttribute("key", value);3.从session中获取数据:

代码语言:javascript代码运行次数:0运行复制 Object value = session.getAttribute("key");4.从session中删除数据:

代码语言:javascript代码运行次数:0运行复制 session.removeAttribute("key");5.使session失效:

代码语言:javascript代码运行次数:0运行复制 session.invalidate();②用户登录、注册注册、登录: 用户将手机号、验证码输入,后台从session中获取验证码与用户输入的验证码进行比对校验,如果不一致则无法通过校验,提示用户验证码错误,验证码一直则后台根据手机号查询用户,若用户不存在,则为用户创建账号信息并保存至数据库中,最后无论用户是否存在,都将用户的信息保存至session中,方便后续业务获取当前用户信息。③校验登录状态 校验登陆状态:

用户在客户端发起请求时,Cookie会携带用户的 JsessionId 后台,后台根据 JsessionId 从session中获取用户信息,如果没有用户信息就表示未登录,会对请求进行拦截,如果有用户信息,将其存入到本地线程 ThreadLocal 中并放行。

为什么使用ThreadLocal:

每个用户其实对应都是去找tomcat线程池中的一个线程来完成工作的, 使用完成后再进行回收,既然每个请求都是独立的,所以在每个用户去访问我们的工程时,我们可以使用threadlocal来做到线程隔离,每个线程操作自己的一份数据。 什么是 JsessionId ?

JSessionId是Java Web应用程序中的一个会话标识符,用于跟踪用户与Web应用程序之间的会话。当用户第一次访问Web应用程序时,服务器会为该用户创建一个唯一的JSessionId,并将其存储在cookie中。在随后的请求中,浏览器会将该cookie发送回服务器,以便服务器可以识别用户并维护会话状态。

在Java Web应用程序中,可以使用HttpSession对象来访问和管理会话状态。

🚀代码实现业务逻辑实现:

统一返回类型 实体类:

代码语言:javascript代码运行次数:0运行复制@Data

@NoArgsConstructor

@AllArgsConstructor

public class Result {

private Boolean success;

private String errorMsg;

private Object data;

private Long total;

public static Result ok(){

return new Result(true, null, null, null);

}

public static Result ok(Object data){

return new Result(true, null, data, null);

}

public static Result ok(List data, Long total){

return new Result(true, null, data, total);

}

public static Result fail(String errorMsg){

return new Result(false, errorMsg, null, null);

}

}校验手机号、邮箱、验证码格式:

代码语言:javascript代码运行次数:0运行复制public class RegexUtils {

/**

* 是否是无效手机格式

* @param phone 要校验的手机号

* @return true:符合,false:不符合

*/

public static boolean isPhoneInvalid(String phone){

return mismatch(phone, RegexPatterns.PHONE_REGEX);

}

/**

* 是否是无效邮箱格式

* @param email 要校验的邮箱

* @return true:符合,false:不符合

*/

public static boolean isEmailInvalid(String email){

return mismatch(email, RegexPatterns.EMAIL_REGEX);

}

/**

* 是否是无效验证码格式

* @param code 要校验的验证码

* @return true:符合,false:不符合

*/

public static boolean isCodeInvalid(String code){

return mismatch(code, RegexPatterns.VERIFY_CODE_REGEX);

}

// 校验是否不符合正则格式

private static boolean mismatch(String str, String regex){

if (StrUtil.isBlank(str)) { //Hutool工具:StrUtil

return true;

}

return !str.matches(regex);

}

}发送短信验证码 业务:

代码语言:javascript代码运行次数:0运行复制 @Override

public Result sendCode(String phone, HttpSession session) {

//1. 手机号不合法?

if(RegexUtils.isPhoneInvalid(phone)){

//2. 不合法,返回错误信息

return Result.fail("手机号格式错误!");

}

//3. 借助工具类,生成验证码(Hutool工具)

String code = RandomUtil.randomNumbers(6);

//4. 保存验证码至session域

session.setAttribute("code",code);

//5. 发送验证码

log.debug("发送短信验证码成功,验证码: " + code); //日志、方便控制台查看

/*

调用验证码服务...(具体逻辑参照具体服务供应商的文档)

*/

//6. 返回ok

return Result.ok();

}登录、注册 业务:

代码语言:javascript代码运行次数:0运行复制 /**

* session实现登录功能

* @param loginForm

* @param session

* @return

*/

@Override

public Result login(LoginFormDTO loginForm, HttpSession session) {

//1. 校验手机号

String phone = loginForm.getPhone();

if(RegexUtils.isPhoneInvalid(phone)){

//2. 返回错误信息

return Result.fail("手机号格式错误");

}

//3. 校验验证码

Object cacheCode = session.getAttribute("code");

String code = loginForm.getCode();

if(code == null || !code.toString().equals(cacheCode)){

//不一致,返回错误信息

return Result.fail("验证码错误");

}

// 一致,根据手机号获取用户

User user = this.query().eq("phone", phone).one(); //(mybatisPlus提供的Service层方法)

//5. 判断用户是否存在

if(user == null){

//6. 不存在,创建新用户

user = new User();

user.setPhone(phone); //设置phone

user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10)); //设置随机昵称

this.save(user); // 存入数据库(mybatisPlus提供的Service层方法)

}

//7. 用户存在,存入session域

session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));

//返回ok

return Result.ok();

}创建并设置ThreadLocal 自定义工具类:

代码语言:javascript代码运行次数:0运行复制public class UserHolder {

private static final ThreadLocal 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();

}

}校验登陆状态 拦截器:

代码语言:javascript代码运行次数:0运行复制/**

* TODO 登录 拦截器

* @author .29.

* @create 2023-11-26 16:37

*/

public class LoginInterceptor implements HandlerInterceptor {

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

// 1.获取session

HttpSession session = request.getSession();

//2. 获取用户

Object user = session.getAttribute("user");

//3. 验证用户是否存在

if(user == null){

//4. 不存在,进行拦截,返回401状态码

response.setStatus(401);

return false;

}

//5. 存在,存入ThreadLocal(自定义工具类UserHolder,作用:创建并设置ThreadLocal)

UserHolder.saveUser((UserDTO) user);

//6. 放行

return true;

}

} Spring Boot使用Spring MVC拦截器的步骤如下:

1.创建一个拦截器类并实现HandlerInterceptor接口,该接口包含三个方法:preHandle、postHandle和afterCompletion。preHandle方法在请求处理之前调用,postHandle方法在请求处理之后调用,afterCompletion方法在视图渲染之后调用。

2.在拦截器类上使用@Component或@Configuration注解将其声明为Spring组件。

3.创建一个配置类并实现WebMvcConfigurer接口,该接口包含一个addInterceptors方法,用于注册拦截器。

4.在addInterceptors方法中使用addInterceptor方法注册拦截器,并使用addPathPatterns方法指定要拦截的请求路径。

使拦截器生效 SpringMvc配置类:

代码语言:javascript代码运行次数:0运行复制/**

* TODO SpringMVC配置类,使拦截器生效

* @author .29.

* @create 2023-11-26 16:49

*/

@Configuration

public class MvcConfig implements WebMvcConfigurer {

@Resource

private StringRedisTemplate stringRedisTemplate;

@Override

public void addInterceptors(InterceptorRegistry registry) {

//1. 添加登录拦截器、同时设置无需拦截的路径

registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(

"/shop/**",

"/voucher/**",

"/shop-type/**",

"/upload/**",

"/blog/hot",

"/user/code",

"/user/login"

).order(0); //order默认0,order值越大拦截器越后执行

}

}