当前位置: 首页 > news >正文

瑞安网站网站建设成都网站排名生客seo怎么样

瑞安网站网站建设,成都网站排名生客seo怎么样,暴雪公司现状,怎么制作wordpress主题目录 一、基于 Session 实现登录(1) 发送短信验证码① 手机号格式后端校验② 生成短信验证码 (2) 短信验证码登录、注册(3) 登录验证① 通过 SpringMVC 定义拦截器② ThreadLocal (4) 集群 Session 不共享问题 二、基于 Redis 实现共享 session 登录(1) 登录之后#xff0c;缓… 目录 一、基于 Session 实现登录(1) 发送短信验证码① 手机号格式后端校验② 生成短信验证码 (2) 短信验证码登录、注册(3) 登录验证① 通过 SpringMVC 定义拦截器② ThreadLocal (4) 集群 Session 不共享问题 二、基于 Redis 实现共享 session 登录(1) 登录之后缓存 token 到客户端(2) 每次请求都携带 token(3) 短信验证码(4) 短信验证码登录、注册(5) 免登录(6) 刷新登录有效期 文章基于 B 站黑马程序员视频教程编写 做笔记便于日后复习 一、基于 Session 实现登录 (1) 发送短信验证码 ① 手机号格式后端校验 手机号校验的正则表达式 /*** 正则表达式*/ 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);} }② 生成短信验证码 dependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.7.17/version/dependencyhutool 工具的详细使用 https://doc.hutool.cn/pages/index/ /*** 发送短信验证码** param phone 手机号* param session 用户缓存验证码*/Overridepublic Result sendCode(String phone, HttpSession session) {// 校验手机号格式是否符合手机号的规范if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail(手机号格式错误);}// 生成 6 位数字短信验证码String code RandomUtil.randomNumbers(6);// 把短信验证码保存到服务端 session 中session.setAttribute(code, code);// 发送短信验证码给手机号 phonelog.info(向 {} 手机号发送了验证码{}, phone, code);return Result.ok(发送验证码成功);}(2) 短信验证码登录、注册 Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {Result result requestParamsValidate(loginForm, session);if (!result.getSuccess()) { // 手机号或验证码校验失败return result;}// 根据手机号查询用户String phone loginForm.getPhone();User user query().eq(phone, phone).one();// 根据手机号在数据中没有查询到用户信息if (user null) {// 注册用户Result saveResult saveUserByPhone(phone);if (saveResult.getSuccess()) { // 注册用户成功user (User) saveResult.getData();} else {return saveResult;}}// 保存用户信息到 sessionsession.setAttribute(user, user);return Result.ok(登录成功);}private Result saveUserByPhone(String phone) {User newUser new User();newUser.setNickName(USER_ RandomUtil.randomString(9));newUser.setPhone(phone);if (save(newUser)) {return Result.ok(newUser);}return Result.fail(服务器忙, 用户保存到数据库失败);}private Result requestParamsValidate(LoginFormDTO loginForm, HttpSession session) {// 校验手机号String phone loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail(手机号格式错误);}// 校验验证码是否存在Object cacheCode session.getAttribute(code);String paramCode loginForm.getCode();if (RegexUtils.isCodeInvalid(paramCode) || !paramCode.equals(cacheCode)) {return Result.fail(验证码错误);}return Result.ok();}(3) 登录验证 根据 Cookie 中的 JSESSIONID 获取到 Session 然后从 Session 中获取到信息 登录校验需要在拦截器Interceptor中完成 SpringMVC 的 Interceptor 可以拦截 Controller在请求到达 Controller 之前做一些事情 ① 通过 SpringMVC 定义拦截器 实现implementsHandlerInterceptor 接口可覆盖该接口中的三个默认方法 preHandle在 Controller 的处理方法之前调用当该方法的返回值为 true 的时候 才执行 Controller 里面的内容 通常在 preHandle 中进行初始化、请求预处理等操作可进行登录验证 preHandle 返回 true 才会执行后面的调用。若返回 false不会调用 Controller 处理方法、postHandle 和 afterCompletion 当有多个拦截器时preHandle 按照正序执行 postHandle在 Controller 的处理方法之后DispatcherServlet 进行视图渲染之前调用 可在 postHandle 中进行请求后续加工处理操作 当有多个拦截器时postHandle 按照逆序执行 afterCompletion在 DispatcherServlet 进行视图渲染之后调用 一般在这里进行资源回收操作 当有多个拦截器时afterCompletion 按照逆序执行 配置拦截器 Configuration public class MvcConfig implements WebMvcConfigurer {/*** 配置拦截器*/Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).excludePathPatterns( // 这些请求不可拦截/shop/**,/voucher/**,/shop-type/**,/upload/**,/blog/hot,/user/code,/user/login);} }② ThreadLocal ThreadLocal 可以解释成线程的局部变量 一个 ThreadLocal 的变量只有当前自身线程可以访问别的线程都访问不了那么自然就避免了线程竞争 ThreadLocal 提供了一种与众不同的线程安全方式它不是在发生线程冲突时想办法解决冲突而是彻底避免了冲突的发生 /*** 用于保存 UserDTO 的 ThreadLocal 的封装*/ public class UserHolder {private static final ThreadLocalUserDTO 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();} }登录拦截器 /*** 登录校验拦截器*/ public class LoginInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception {HttpSession session request.getSession();Object user session.getAttribute(user);if (null user) {response.setStatus(401);return false; // 拦截}UserDTO userDTO user2UserDto((User) user);// 保存用户信息到 ThreadLocal 中UserHolder.saveUser(userDTO);return true;}private UserDTO user2UserDto(User user) {UserDTO userDTO new UserDTO();userDTO.setIcon(user.getIcon());userDTO.setId(user.getId());userDTO.setNickName(user.getNickName());return userDTO;}/*** 资源释放*/Overridepublic void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex) throws Exception {UserHolder.removeUser();}Overridepublic void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView) throws Exception {} }GetMapping(/me)public Result me() {// 获取当前登录的用户并返回UserDTO userDto UserHolder.getUser();return Result.ok(userDto);}/me 这个 Controller 执行完毕后LoginInterceptor 的 afterCompletion 会被调用 (4) 集群 Session 不共享问题 二、基于 Redis 实现共享 session 登录 登录之后每次发起请求都要携带 token用户登录凭证 (1) 登录之后缓存 token 到客户端 login() {const {radio, phone, code} this.formif (!radio) {this.$message.error(请先确认阅读用户协议);return}if (!phone || !code) {this.$message.error(手机号和验证码不能为空);return}if (phone.length ! 11) {this.$message.error(手机号格式错误);return}axios.post(/user/login, this.form).then(({data}) {if (data) {// 保存用户信息到 sessionsessionStorage.setItem(token, data);}// 跳转到首页location.href /info.html}).catch(err {console.log(err)this.$message.error(err)})},(2) 每次请求都携带 token let commonURL /api; // 设置后台服务地址 axios.defaults.baseURL commonURL axios.defaults.timeout 2000// request 拦截器将用户 token 放入头中 let token sessionStorage.getItem(token)// 请求拦截器 axios.interceptors.request.use(config {// 如果 token 存在, 将用户 token 放入头中key 是 authorizationif (token) config.headers[authorization] tokenreturn config},error {console.log(error)return Promise.reject(error)} )// 响应拦截器 axios.interceptors.response.use(function (response) {// 判断执行结果if (!response.data.success) {return Promise.reject(response.data.errorMsg)}return response.data; }, function (error) {// 一般是服务端异常或者网络异常console.log(error)if (error.response.status 401) {// 未登录跳转setTimeout(() {location.href /login.html}, 200);return Promise.reject(请先登录);}return Promise.reject(服务器异常); });// 请求参数序列化 axios.defaults.paramsSerializer function (params) {let p ;Object.keys(params).forEach(k {if (params[k]) {p p k params[k]}})return p; }在 axios 的请求拦截器中配置 token每次发起请求该请求会首先被拦截 被拦截之后往该请求的请求头中设置 token【key: authorizationvalue: token 值】 (3) 短信验证码 Redis 相关常量 public class RedisConstants {public static final String LOGIN_CODE_KEY login:code:phone;public static final Long LOGIN_CODE_TTL 2L; // 验证码有效期public static final String LOGIN_USER_KEY login:token:;public static final Long LOGIN_USER_TTL 36000L;public static final Long CACHE_NULL_TTL 2L;public static final Long CACHE_SHOP_TTL 30L;public static final String CACHE_SHOP_KEY cache:shop:;public static final String LOCK_SHOP_KEY lock:shop:;public static final Long LOCK_SHOP_TTL 10L;public static final String SECKILL_STOCK_KEY seckill:stock:;public static final String BLOG_LIKED_KEY blog:liked:;public static final String FEED_KEY feed:;public static final String SHOP_GEO_KEY shop:geo:;public static final String USER_SIGN_KEY sign:; }public Result sendCode(String phone, HttpSession session) {// 校验手机号格式是否符合手机号的规范if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail(手机号格式错误);}// 生成 6 位数字短信验证码String code RandomUtil.randomNumbers(6);// 把短信验证码保存到 Redis 中stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY phone,code,RedisConstants.LOGIN_CODE_TTL,TimeUnit.MINUTES);// 发送短信验证码给手机号 phonelog.info(向 {} 手机号发送了验证码{}, phone, code);return Result.ok(发送验证码成功);}(4) 短信验证码登录、注册 Slf4j Service public class UserServiceImpl extends ServiceImplUserMapper, User implements IUserService {Resourceprivate StringRedisTemplate stringRedisTemplate;Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {Result result requestParamsValidate(loginForm, session);if (!result.getSuccess()) { // 手机号或验证码校验失败return result;}// 根据手机号查询用户String phone loginForm.getPhone();User user query().eq(phone, phone).one();// 根据手机号在数据中没有查询到用户信息if (user null) {// 注册用户Result saveResult saveUserByPhone(phone);if (saveResult.getSuccess()) { // 注册用户成功user (User) saveResult.getData();} else {return saveResult;}}// 保存用户信息到 Redis// 生成随机 token 串String token RedisConstants.LOGIN_USER_KEY UUID.randomUUID().toString(true);// 把 User 转换为 UserDTO过滤敏感数据UserDTO userDTO user2UserDto(user);// 把 UserDTO 转换为 HashMap便于往 Redis 中存储MapString, Object userDtoMap BeanUtil.beanToMap(userDTO,new HashMap(),CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) - fieldValue.toString()));// 把用户信息保存到 Redis// stringRedisTemplate 要求存储的 key 和 value 都必须是 String 类型// 否则会报类型转换错误stringRedisTemplate.opsForHash().putAll(token, userDtoMap);// 设置登录有效期stringRedisTemplate.expire(token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);session.setAttribute(user, user);return Result.ok(token);}private UserDTO user2UserDto(User user) {UserDTO userDTO new UserDTO();userDTO.setIcon(user.getIcon());userDTO.setId(user.getId());userDTO.setNickName(user.getNickName());return userDTO;}private Result saveUserByPhone(String phone) {User newUser new User();newUser.setNickName(USER_ RandomUtil.randomString(9));newUser.setPhone(phone);if (save(newUser)) {return Result.ok(newUser);}return Result.fail(服务器忙, 用户保存到数据库失败);}private Result requestParamsValidate(LoginFormDTO loginForm, HttpSession session) {// 校验手机号String phone loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail(手机号格式错误);}// 通过手机号获取验证码String redisCode stringRedisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY phone);// 校验验证码是否存在String paramCode loginForm.getCode();if (RegexUtils.isCodeInvalid(paramCode) || !paramCode.equals(redisCode)) {return Result.fail(验证码错误);}return Result.ok();}}(5) 免登录 需要实现一个效果用户只有在使用该应用哪么该用户的登录有效期就延长七天 而不是到达七天就退出登录 在 LoginInterceptor 中若用户有发请求就把该用户的登录有些时间的缓存更新为七天 /*** 登录校验拦截器*/ public class LoginInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate stringRedisTemplate;}Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception {// 获取请求头中的 token 串String token request.getHeader(authorization);if (StrUtil.isBlank(token)) {response.setStatus(401);return false; // 拦截}// 通过前端传过来的 token 串从 Redis 中获取用户信息MapObject, Object userMap stringRedisTemplate.opsForHash().entries(token);if (userMap.isEmpty()) {response.setStatus(401);return false; // 拦截}// 保存用户信息到 ThreadLocal 中// 把 HashMap 类型转换为 UserDto 类型UserDTO userDTO BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);UserHolder.saveUser(userDTO);// 刷新登录有效期只要用户是活跃的, 登录有效期就延长七天stringRedisTemplate.expire(token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);return true;}/*** 资源释放*/Overridepublic void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex) throws Exception {UserHolder.removeUser();}Overridepublic void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView) throws Exception {} }LoginInterceptor 拦截器并没有被 IoC 管理所以不能在 LoginInterceptor 中使用 Resource 和 Autowired Configuration public class MvcConfig implements WebMvcConfigurer {Resourceprivate StringRedisTemplate stringRedisTemplate;/*** 配置拦截器*/Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor(stringRedisTemplate)).excludePathPatterns( // 这些请求不可拦截/shop/**,/voucher/**,/shop-type/**,/upload/**,/blog/hot,/user/code,/user/login);} }(6) 刷新登录有效期 RefreshInterceptor 会拦截每一个请求然后进行登录有效期刷新 上一节中的 LoginInterceptor 只有当访问需要登录校验的请求的时候才会刷新 /*** 刷新登录拦截器每个请求都要来到这里*/ public class RefreshLoginInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public RefreshLoginInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate stringRedisTemplate;}Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception {// 获取请求头中的 token 串String token request.getHeader(authorization);if (StrUtil.isBlank(token)) {return true; // 拦截进入 LoginInterceptor}// 通过前端传过来的 token 串从 Redis 中获取用户信息MapObject, Object userMap stringRedisTemplate.opsForHash().entries(token);if (userMap.isEmpty()) {return true; // 拦截进入 LoginInterceptor}// 保存用户信息到 ThreadLocal 中// 把 HashMap 类型转换为 UserDto 类型UserDTO userDTO BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);UserHolder.saveUser(userDTO);// 刷新登录有效期只要用户是活跃的, 登录有效期就延长七天stringRedisTemplate.expire(token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);return true;}/*** 资源释放*/Overridepublic void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex) throws Exception {UserHolder.removeUser();}Overridepublic void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView) throws Exception {} }/*** 登录校验拦截器*/ public class LoginInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception {if (UserHolder.getUser() null) {response.setStatus(401);return false;}return true;}}Configuration public class MvcConfig implements WebMvcConfigurer {Resourceprivate StringRedisTemplate stringRedisTemplate;/*** 配置拦截器*/Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).excludePathPatterns( // 这些请求不可拦截/shop/**,/voucher/**,/shop-type/**,/upload/**,/blog/hot,/user/code,/user/login);// order 值越小优先级越高// order 值默认是 0registry.addInterceptor(new RefreshLoginInterceptor(stringRedisTemplate)).addPathPatterns(/**).order(-1);} }
http://www.hkea.cn/news/14584332/

相关文章:

  • 如何做网站栏目规划洛阳网站建设哪个好点
  • 网页内嵌网站浙江政务服务网官网
  • 上海外贸网站google建站佛山seo网站优化
  • 网站建设关键技术视觉设计专业
  • 做网站用什么数据库北京市建设工程信息网交易网站
  • 灵武住房和城乡建设厅网站制作网页哪家好
  • Fastcgi做网站开办网站备案
  • 织梦后台怎么建设网站北辰集团网站建设
  • 网易官网建立个人网站电台 主题 wordpress
  • jsp网站开发中英文页面切换目前有哪些跨境电商平台
  • 智能网站建设报价网站建设着
  • 国内网站设计案例族蚂建站怎么样
  • 网站备案要幕布车培训网站建设
  • 网店网站设计论文网页制作教程第三版赵丰年pdf
  • 在虚拟机中如何做二级域名网站上海电商网站建设费用
  • 网站哪个公司好网站模版参考
  • 济南做网站的价格wordpress公司主题破解版
  • 潍坊市作风建设年活动网站如何写手机适配网站
  • 高邮企业网站建设广州 骏域网站建设 陶瓷
  • 深圳制作网站推荐一起做网站注册地址
  • 用dw做的网站怎么发布到网上银州手机网站建设
  • 做网站怎么在国外服务器租用泰安东平房产信息网
  • 备案通过后 添加网站做购买网站
  • 动漫网站建设规划书模板贵阳网站建设葫芦岛
  • 注册网站商标多少钱深圳沙头角网站建设
  • 河南省建设执业资格注册中心网站cn免费域名注册网站
  • 福安市教育局建设网站足球世界排名
  • 潍坊知名网站建设服务商wordpress没有插件
  • 域名解析到别的网站乐温州网站建设
  • 周口在线网站建设免费做初中试卷的网站