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

网站开发的外文翻译兼职招聘信息最新招聘

网站开发的外文翻译,兼职招聘信息最新招聘,wordpress 插件 知乎,手工制作大全创意废物利用目录 整体流程 发送验证码 短信验证码登录、注册 校验登录状态 基于 session 实现登录 实现发送短信验证码功能 1. 前端发送请求 2. 后端处理请求 3. 演示 实现登录功能 1. 前端发送请求 2. 后端处理请求 校验登录状态 1. 登录拦截器 2. 注册拦截器 3. 登录完整…                                                目录 整体流程 发送验证码 短信验证码登录、注册 校验登录状态 基于 session 实现登录 实现发送短信验证码功能 1. 前端发送请求 2. 后端处理请求 3. 演示 实现登录功能 1. 前端发送请求 2. 后端处理请求 校验登录状态 1. 登录拦截器 2. 注册拦截器 3. 登录完整演示 session共享问题 问题描述 基于 Redis 实现登录 设计 key 的结构 整体访问流程 基于 session 登录-流程图 基于 Redis 实现共享 session 登录-流程图 代码实现 发短信 登录功能 拦截器功能修改 登录演示 解决状态登录刷新问题 优化方案 代码修改 第一个拦截器 第二个拦截器 涉及到 order 的源码​​​​​​ 整体流程 基础模版如下可以根据具体需求进行修改和拓展 发送验证码 用户在提交手机号后会校验手机号是否合法如果不合法则要求用户重新输入手机号如果手机号合法后台此时生成对应的验证码同时将验证码进行保存然后再通过短信的方式将验证码发送给用户 短信验证码登录、注册 用户将验证码和手机号进行输入后台从 session 中拿到当前验证码然后和用户输入的验证码进行校验 如果不一致则无法通过校验 如果一致则后台根据手机号查询用户如果用户不存在则为用户创建账号信息保存到数据库无论是否存在都会将用户信息保存 session 中方便后续获得当前登录信息 校验登录状态 用户在请求时候会从 cookie 中携带者 sessionId 到后台后台通过 sessionId 从 session 中拿到用户信息如果没有 session 信息则进行拦截如果有 session 信息则将用户信息保存到 ThreadLocal 中并且放行。 基于 session 实现登录 实现发送短信验证码功能 1. 前端发送请求 前端输入电话点击发送验证码发送请求 2. 后端处理请求 Controller PostMapping(code)public Result sendCode(RequestParam(phone) String phone, HttpSession session) {// 发送短信验证码并保存验证码return userService.sendCode(phone, session);} Service Result sendCode(String phone, HttpSession session); ServiceImpl /*** 发送验证码* param phone* param session* return*/Overridepublic Result sendCode(String phone, HttpSession session) {// 1、校验手机号if (RegexUtils.isPhoneInvalid(phone)) {// 2. 如果不符合 返回错误信息return Result.fail(手机号格式错误!);}// 3. 符合生成验证码// hutool 工具类 随机生成一个6位数的号码String code RandomUtil.randomNumbers(6);// 4. 保存验证码到sessionsession.setAttribute(code,code);// 5. 发送验证码log.debug(模拟发送短信验证码成功验证码{},code);// 结束return Result.ok();} 3. 演示 后端生成验证码 实现登录功能 1. 前端发送请求 从后端获取到验证码 2. 后端处理请求 操作数据库是用的技术是 Mybatis-Plus Controller PostMapping(/login)public Result login(RequestBody LoginFormDTO loginForm, HttpSession session){// 实现登录功能return userService.login(loginForm, session);} Service Result login(LoginFormDTO loginForm, HttpSession session); ServiceImpl /*** 登录* param loginForm* param session* return*/Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {// 1.检验手机号String phone loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {// 2. 如果不符合 返回错误信息return Result.fail(手机号格式错误!);}// 3. 校验验证码// 从session中获取刚刚生成的验证码Object cacheCode session.getAttribute(code);// 获取从前端发送过来的验证码String code loginForm.getCode();if (cacheCode null || !cacheCode.toString().equals(code)) {// 验证码和手机不一致return Result.fail(验证码错误!);}// 4. 验证码和手机一致根据手机号查询用户 // 使用了 Mybatis-Plus User user query().eq(phone, phone).one();if (user null) {// 5. 不存在创建新用户并保存user createUserWithPhone(phone);}// 7. 保存用户信息到 session 中UserDTO userDTO new UserDTO();// 8. 用户信息脱敏BeanUtils.copyProperties(user, userDTO);session.setAttribute(user, userDTO);log.debug(保存用户信息到session user {}, user);return Result.ok();} 不熟悉 MybasitPlus 的小伙伴可以看看这个文章快速入个小门 快速熟悉MybatisPlus Mybatis的使用与区别_mybatis和mybatis-plus-CSDN博客 UserDTO Data public class UserDTO {private Long id;private String nickName;private String icon; } createUserWithPhone 方法 -- 新增用户 private User createUserWithPhone(String phone) {// 1. 创建一个新用户User user new User();user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX RandomUtil.randomString(10));// 2. 保存用户save(user);log.debug(创建新用户成功用户信息{},user);return user; } 注 根据前端需要渲染的页面建立相关的 DTO 类做用户信息脱敏返回一些主要的信息给前端就行。 减少 session 存储用户所占用的内存大小从而减小服务器的压力(session 是存在服务器端的嘛)。数据量小得话在网络传输信息的时候花的时间也会更少加快服务端的响应降低网络对服务的影响使用户体验更加。 校验登录状态 温馨小贴士tomcat的运行原理 当用户发起请求时会访问我们像tomcat注册的端口任何程序想要运行都需要有一个线程对当前端口号进行监听tomcat 也不例外当监听线程知道用户想要和 tomcat 连接连接时那会由监听线程创建 socket 连接socket都是成对出现的用户通过socket像互相传递数据当tomcat端的socket接受到数据后此时监听线程会从tomcat的线程池中取出一个线程执行用户请求在我们的服务部署到tomcat后线程会找到用户想要访问的工程然后用这个线程转发到工程中的controllerservicedao 中并且访问对应的 DB在用户执行完请求后再统一返回再找到 tomcat 端的 socket 再将数据写回到用户端的 socket 完成请求和响应 通过上述我们可以得知每个用户其实对应都是去找 tomcat 线程池中的一个线程来完成工作的 使用完成后再进行回收既然每个请求都是独立的所以在每个用户去访问我们的工程时我们可以使用。ThreadLocal 来做到线程隔离每个线程操作自己的一份数据。 因为涉及到线程池给大家推荐一篇池化技术的文章可以简单了解了解池化技术拓宽自己的知识哈 概述池化技术-CSDN博客 1. 登录拦截器 import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.xml.ws.handler.Handler;/*** author lhd* date 2024/7/26* apiNote*/ public class LoginInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 获取sessionHttpSession session request.getSession();// 2. 从 session 中获取用户的状态信息// 用户信息脱敏UserDTO user (UserDTO) session.getAttribute(user);// 3. 判断用户是否存在if (user null){// 不存在就拦截response.setStatus(401);return false;}// 5. 存在 保存用户信息到 ThredLocalUserHolder.saveUser(user);// 6. 放行return true;}Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);} }UserHolder 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();} }2. 注册拦截器 /*** author lhd* date 2024/7/26* apiNote*/ Configuration public class MvcConfig implements WebMvcConfigurer {Overridepublic void addInterceptors(InterceptorRegistry registry) {// 登录拦截器registry.addInterceptor(new LoginInterceptor())// 不拦截的路径.excludePathPatterns(/user/code,/user/login,/blog/hot,/shop/**,/shop-type/**,/voucher/**);WebMvcConfigurer.super.addInterceptors(registry);} } 3. 登录完整演示 登录跳转到个人中心页面 因为这是一个新用户后台数据库也会插入这个用户的信息 session共享问题 问题描述 每个 tomcat 中都有一份属于自己的 session ,假设用户第一次访问第一台 tomcat并且把自己的信息存放到第一台服务器的 session 中但是第二次这个用户访问到了第二台 tomcat那么在第二台服务器上肯定没有第一台服务器存放的 session所以此时整个登录拦截功能就会出现问题。 我们能如何解决这个问题呢 早期的方案是 session拷贝就是说虽然每个 tomcat 上都有不同的 session但是每当任意一台服务器的 session 修改时都会同步给其他的 tomcat 服务器的 session这样的话就可以实现session的共享了。 但是这种方案具有两个大问题 1、每台服务器中都有完整的一份session数据服务器压力过大。 2、session拷贝数据时可能会出现延迟 模拟项目架构图 综上所述我们可以选用更好的方案使用 redis所以咱们后来采用的方案都是基于 redis 来完成我们把 session 换成 redisredis 数据本身就是共享的就可以避免 session共享的问题了 基于 Redis 实现登录 设计 key 的结构 首先我们要思考一下利用 redis 来存储数据那么到底使用哪种结构呢 由于存入的数据比较简单我们可以考虑使用 String 或者是使用哈希如下图如果使用 String注意他的value用多占用一点空间如果使用哈希则他的 value 中只会存储他数据本身如果不是特别在意内存其实使用 String 就可以啦。 所以我们可以使用 String 结构就是一个简单的 keyvalue 键值对的方式但是关于 key 的处理session 他是每个用户都有自己的 session但是 redis 的 key 是共享的咱们就不能使用 code(验证码) 了。 在设计这个 key 的时候我们之前讲过需要满足两点         1、key要具有唯一性         2、key要方便携带 如果我们采用 phone手机号 作为key用这个 key 来存储当然是可以的但是如果把这样的敏感数据存储到 redis 中并且从页面中带过来毕竟不太合适。 所以我们在后台生成一个随机串token然后让前端带来这个 token 就能完成我们的整体逻辑了。 整体访问流程 基于 session 登录-流程图 基于 Redis 实现共享 session 登录-流程图 总的来说总体功能没变只是功能实现发生了改变。 主要变化 使用 redis 完成用户登录后需要返回一个随机 token 给前端以便于用户后续访问。发送请求从携带 cookie 转变为携带后端生成的随机 token。获取用户信息从 session 中获取转变为通过 token从 redis 中获取。获取验证码从 session 中获取转变为以手机号为 key 从 redis 中获取。 代码实现 发短信 Resourceprivate StringRedisTemplate stringRedisTemplate;/*** 发送验证码* param phone* param session* return*/Overridepublic Result sendCode(String phone, HttpSession session) {// 1、校验手机号if (RegexUtils.isPhoneInvalid(phone)) {// 2. 如果不符合 返回错误信息return Result.fail(手机号格式错误!);}// 3. 符合生成验证码// hutool 工具类 随机生成一个6位数的号码String code RandomUtil.randomNumbers(6);// 4. 保存验证码到session// session.setAttribute(code,code);// 4.以 String 数据结构 保存验证码到 redis中stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);// 5. 发送验证码log.debug(模拟发送短信验证码成功验证码{},code);// 结束return Result.ok();} 登录功能 /*** 登录* param loginForm* param session* return*/Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {// 1.检验手机号String phone loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {// 2. 如果不符合 返回错误信息return Result.fail(手机号格式错误!);}// 3. 从redis获取验证码并校验String cacheCode stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY phone);String code loginForm.getCode();if (cacheCode null || !cacheCode.equals(code)) {// 验证码不一致return Result.fail(验证码错误!);}// 4.一致根据手机号查询用户User user query().eq(phone, phone).one();if (user null) {// 5. 不存在创建新用户并保存user createUserWithPhone(phone);}// 7.保存用户信息到 redis 中// 7.1 随机生成 token 作为登录令牌String token UUID.randomUUID().toString(true);// 7.2.将User对象转为HashMap存储// BeanUtil 是 hutool 工具类中的 复制 user 的信息 到 脱敏 UserDTO 中UserDTO userDTO BeanUtil.copyProperties(user, UserDTO.class); // MapString, Object userMap BeanUtil.beanToMap(userDTO);MapString, Object userMap BeanUtil.beanToMap(userDTO, new HashMap(),CopyOptions.create()// 忽略空值错误.setIgnoreNullValue(true)// 自定义转换器 因为 userDTO 中的 id 为 long 类型.setFieldValueEditor((fieldName, fieldValue) - fieldValue.toString()));// 7.3 存储String tokenKey LOGIN_USER_KEY token;// 以 Hash 数据结构存储用户信息// login:token: token 生成存储字符串作为 key 保存当前用户的信息到 redis中stringRedisTemplate.opsForHash().putAll(tokenKey token, userMap);// 7.4 设置 token 有效期stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);log.debug(保存用户信息到 redis user {}, userDTO);// 8. 返回 tokenreturn Reslt.ok(token);} 拦截器功能修改 LoginInterceptor Slf4j public class LoginInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;// 因为这个类不属于 spring 管理的类所以需要通过构造器去获取 StringRedisTemplate类// 构造器注入的方式 挺不错的思想public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate stringRedisTemplate;}Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 获取请求头中的 tokenlog.debug(拦截器被调用);String token request.getHeader(authorization);System.out.println(从头获取到的token token);if (StrUtil.isBlank(token)){// token为空说明没有登录过return false;}// 2.基于TOKEN获取redis中的用户String key LOGIN_USER_KEY token;MapObject, Object userMap stringRedisTemplate.opsForHash().entries(key);// 3. 判断用户是否存在if (userMap.isEmpty()){// 不存在就拦截response.setStatus(401);return false;}// 5. 将查询到的 Hash 数据转为 UserDTO对象// 不忽略转换的错误UserDTO user BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);// 6. 保存用户信息到 ThreadLocalUserHolder.saveUser(user);// 7. 刷新 token 有效期stringRedisTemplate.expire(key, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);log.debug(ThreadLocal 中的User为: user);// 8. 放行return true;} MvcConfig Configuration public class MvcConfig implements WebMvcConfigurer {// 本类加了 Configuration 项目启动时候会进行自动装配 可以获取到 StringRedisTemplateResourceprivate StringRedisTemplate stringRedisTemplate;Overridepublic void addInterceptors(InterceptorRegistry registry) {// 登录拦截器registry.addInterceptor(new LoginInterceptor(stringRedisTemplate))// 不拦截的路径.excludePathPatterns(/user/code,/user/login,/upload/**,/blog/hot,/shop/**,/shop-type/**,/voucher/**);} } 抛了个类型转换异常 原因 因为 StringRedisTemplate key 和 value 都需要 String 类型 解决方案 一开始 UserDTO 直接转 map出了一个类型转换异常需要自定义一下转换规则login 实现类中将 UserDTO 转换为 map 存入 redis 之前做个调整将所有 value 都转为 String 类型在存入map // MapString, Object userMap BeanUtil.beanToMap(userDTO);MapString, Object userMap BeanUtil.beanToMap(userDTO, new HashMap(),CopyOptions.create()// 忽略空值错误.setIgnoreNullValue(true)// 自定义转换器 因为 userDTO中的 id 为 long 类型.setFieldValueEditor((fieldName, fieldValue) - fieldValue.toString())); 登录演示 redis中的数据 验证码 token 数据库数据 前端页面 --- 登录成功 进入个人主页 解决状态登录刷新问题 在这个方案中他确实可以使用对应路径的拦截同时刷新登录token令牌的存活时间但是现在这个拦截器他只是拦截需要被拦截的路径假设当前用户访问了一些不需要拦截的路径那么这个拦截器就不会生效所以此时令牌刷新的动作实际上就不会执行所以这个方案他是存在问题的。 优化方案 既然之前的拦截器无法对不需要拦截的路径生效那么我们可以添加一个拦截器 在第一个拦截器中拦截所有的路径把第二个拦截器做的事情放入到第一个拦截器中同时刷新令牌因为第一个拦截器有了 ThreadLocal 的数据所以此时第二个拦截器只需要判断拦截器中的user对象是否存在即可完成整体刷新功能。 总的来说 第一个拦截器负责刷新 token 。第二个拦截器负责拦截需要登录的页面校验用户当前是否登录。 代码修改 第一个拦截器 刷新token /*** author lhd* date 2024/7/26* apiNote*/ Slf4j public class RefreshTokenInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate stringRedisTemplate;}Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 获取请求头中的 tokenlog.debug(拦截器被调用);String token request.getHeader(authorization);System.out.println(从头获取到的token token);if (StrUtil.isBlank(token)){return true;}// 2.基于TOKEN获取redis中的用户String key LOGIN_USER_KEY token;MapObject, Object userMap stringRedisTemplate.opsForHash().entries(key);// 3. 判断用户是否存在if (userMap.isEmpty()){return true;}// 5. 将查询到的 Hash 数据转为 UserDTO对象// 不忽略转换的错误UserDTO user BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);// 6. 保存用户信息到 ThreadLocalUserHolder.saveUser(user);// 7. 刷新 token 有效期stringRedisTemplate.expire(key, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);log.debug(ThreadLocal 中的User为: user);// 8. 放行return true;}Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 移除用户UserHolder.removeUser();} }第二个拦截器 拦截需要登录的 /*** author lhd* date 2024/7/26* apiNote*/ Slf4j public class LoginInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 判断是否需要拦截(ThreadLocal中是否有用户)if (UserHolder.getUser() null){// 没有信息需要拦截设置状态码response.setStatus(401);// 拦截return false;}// 有用户信息 则放行return true;}}MVCconfig 设置 order 是为了让第一个拦截器先执行(0的优先级最大)。 /*** author lhd* date 2024/7/26* apiNote*/ Configuration public class MvcConfig implements WebMvcConfigurer {Resourceprivate StringRedisTemplate stringRedisTemplate;Overridepublic void addInterceptors(InterceptorRegistry registry) {// 登录拦截器registry.addInterceptor(new LoginInterceptor())// 不拦截的路径.excludePathPatterns(/user/code,/user/login,/upload/**,/blog/hot,/shop/**,/shop-type/**,/voucher/**).order(1);registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns(/**).order(0);} }注 拦截器都不设置 order 的参数就是默认为0, 默认就按照添加顺序实现拦截。 如果要实现第一个拦截器先执行就把他放在前面。 代码如下 // token刷新的拦截器registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns(/**);// 登录拦截器registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(/shop/**,/voucher/**,/shop-type/**,/upload/**,/blog/hot,/user/code,/user/login); 如果更加严谨的话就设置 order(0) 涉及到 order 的源码 进入 进入 找到order 后期会更新使用阿里云发送短信
http://www.w-s-a.com/news/507996/

相关文章:

  • 凡科网站建设网站wordpress 七牛oss
  • 搬瓦工的主机可以用来做网站吗分类信息网站开发需求方案
  • 上海高端网站开发站霸网络国际网站建设的目的
  • 程序员招聘求职的网站做网站加入广告联盟
  • 网站建设的技术方案模板易做文学网站的logo
  • 建设国家标准官方网站响应式网站切图
  • 网站链接数怎么做wordpress安装网址
  • 沈阳建网站 哪家好如何做旅游网站推销
  • 继续网站建设南通网站建设方法
  • 淮南公司网站建设如果做京东优惠卷的网站
  • 二手房网站平台怎么做项目工程监理公司网站建设方案
  • 秦皇岛做网站公司小说推广平台有哪些
  • php网站做分享到朋友圈天元建设集团有限公司信用代码
  • 邱县做网站在线免费图片编辑器
  • 网站备份网站做网站如何把支付宝微信吧
  • 做网站的怎么获取客户信息晋城建设局网站
  • 新开传奇网站发布网单职业wordpress建站网页无法运作
  • 海南省住房和城乡建设厅官方网站网站开发有哪些语言
  • 网站开发排期表免费网站建设策划
  • 飞沐网站设计江苏建设人才网证书查询
  • 网站优化的意义怎么帮商家推广赚钱
  • 安顺公司做网站福州建设发展集团有限公司网站
  • 普陀企业网站建设做散客机票的网站如何推广
  • 河北网站建设与制作建设宁波市分行的互联网网站
  • python做网站是不是特别慢百度推广基木鱼
  • 卖网站链接东营住房和城乡建设信息网
  • 网站后台如何上传ico图标单位建设网站需要的材料
  • 如何建淘客网站郑州做网站最好的公司
  • 连锁酒店网站方案o2o网站建设方案
  • 功能型网站响应式网站原理