基于Session实现登录
发送短信验证码
@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);
return Result.ok();
}
校验手机号工具类
-
正则表达式
public abstract class RegexPatterns { /** * 手机号正则 */ public static final String PHONE_REGEX = "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$"; /** * 邮箱正则 */ public static final String EMAIL_REGEX = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$"; /** * 密码正则。4~32位的字母、数字、下划线 */ public static final String PASSWORD_REGEX = "^\\w{4,32}$"; /** * 验证码正则, 6位数字或字母 */ public static final String VERIFY_CODE_REGEX = "^[a-zA-Z\\d]{6}$"; }
-
工具类
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)) { return true; } return !str.matches(regex); } }
短信验证码登录、注册
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
// 1、校验手机号和验证码
String phone = loginForm.getPhone();
if (RegexUtils.isPhoneInvalid(phone)){
// 1.2、如果不符合返回错误信息
return Result.fail("手机号格式错误!");
}
// 2、校验验证码
Object cacheCode = session.getAttribute("code");
String code = loginForm.getCode();
if (cacheCode == null || !cacheCode.toString().equals(code)){
// 3、不一致,报错
return Result.fail("验证码错误!");
}
// 4、一致,根据手机号查询用户
User user = query().eq("phone", phone).one();
// 5、判断用户是否存在
if (user == null) {
// 6、不存在,创建新用户并保存
user = createUserWithPhone(phone);
}
// 7、保存用户到Session 使用UserDto防止暴露敏感信息
session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));
return Result.ok();
}
private User createUserWithPhone(String phone) {
// 1、创建用户
User user = new User();
user.setPhone(phone);
user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(8));
// 2、保存用户
save(user);
return user;
}
校验登录状态
-
引入ThreadLocal
- 将拦截器拦截到的用户信息传入controller
- 线程安全
-
编写UserHolder定义ThreadLocal常量
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(); } }
-
配置拦截器
@Configuration public class MvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .excludePathPatterns( // 放行 "/shop/**", "/voucher/**", "/shop-type/**", "/upload/**", "/blog/**", "/user/code", "/user/login" ); } }
-
编写拦截器逻辑
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) { // 4、不存在,拦截 返回401状态码 response.setStatus(401); return false; } // 5、存在,保存用户信息到ThreadLocal UserHolder.saveUser((UserDTO) user); // 6、放行 return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 移除用户 UserHolder.removeUser(); } }
-
编写接口
@GetMapping("/me") public Result me(){ UserDTO user = UserHolder.getUser(); return Result.ok(user); }