新手做网站服务器用什么,wordpress 微信服务号,怎么用默认程序做网站,Wordpress虚拟域名文章目录 前言一、隐藏用户敏感信息二、短信验证登录、注册1.流程2.代码3.使用redis优化解决代码 二、登录拦截#xff08;校验#xff09;1.流程2.代码 总结 前言 短信登录核心知识 首先黑马点评这个短信登录是一伪验证#xff0c;即后台调用工具类随机生成六位数字。
1.R… 文章目录 前言一、隐藏用户敏感信息二、短信验证登录、注册1.流程2.代码3.使用redis优化解决代码 二、登录拦截校验1.流程2.代码 总结 前言 短信登录核心知识 首先黑马点评这个短信登录是一伪验证即后台调用工具类随机生成六位数字。
1.Redis代替session共享满足多tomcat共享数据。
2.登录拦截、线程隔离实现每个线程操控自己线程的数据与登录状态刷新问题解决。
3.创建新对象解决隐藏用户敏感信息。 一、隐藏用户敏感信息 首先讲诉创建新对象解决隐藏用户敏感信息的问题方便后面讲解。思考正常登录成功后我们通过浏览器观察到此时用户的全部信息都在这样极为不靠谱所以我们应当在返回用户信息之前将用户的敏感信息进行隐藏。解决采用的核心思路就是书写一个UserDto对象这个UserDto对象就没有敏感信息了我们在返回前将有用户敏感信息的User对象转化成没有敏感信息的UserDto对象那么就能够避免这个尴尬的问题了。二次封装但是此次封装对象只包含一些用户简单粗略的信息如头像用户名等。 Data
public class UserDTO {private Long id;private String nickName;private String icon;
}在UserHolder处将所有User对象换成UserDTO
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();}
}这里分UserHolder是一个将用户对象存储到线程中的类并包含获取当前线程用户对象和删除方的法。下面会讲解。 二、短信验证登录、注册
1.流程 基于session实现 发送验证码——短信验证登录未注册自动注册——校验登录状态 发送验证码用户在提交手机号后会校验手机号是否合法如果不合法则要求用户重新输入手机号。如果手机号合法后台此时生成对应的验证码同时将验证码进行保存然后再通过短信的方式将验证码发送给用。短信验证登录、注册用户将验证码和手机号进行输入后台从session中拿到当前验证码然后和用户输入的验证码进行校验如果不一致则无法通过校验如果一致则后台根据手机号查询用户如果用户不存在则为用户创建账号信息保存到数据库无论是否存在都会将用户信息保存到session中方便后续获得当前登录信息。校验登录状态用户在请求时候会从cookie中携带者JsessionId到后台后台通过JsessionId从session中拿到用户信息如果没有session信息则进行拦截如果有session信息则将用户信息保存到threadLocal中并且放行。
2.代码 发送验证码 Overridepublic Result sendCode(String phone, HttpSession session) {// 1.校验手机号if (RegexUtils.isPhoneInvalid(phone)) {// 2.如果不符合返回错误信息return Result.fail(手机号格式错误);}// 3.符合生成验证码工具类String code RandomUtil.randomNumbers(6);// 4.保存验证码到 sessionsession.setAttribute(code,code);// 5.发送验证码log.debug(发送短信验证码成功验证码{}, code);// 返回okreturn Result.ok();}登录校验、登录和注册 Overridepublic 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(cacheCode null || !cacheCode.toString().equals(code)){//3.不一致报错return Result.fail(验证码错误);}//一致根据手机号查询用户User user query().eq(phone, phone).one();//5.判断用户是否存在if(user null){//不存在则创建user createUserWithPhone(phone);}//7.保存用户信息到session中session.setAttribute(user,user);return Result.ok();}
private User createUserWithPhone(String phone) {// 1.创建用户User user new User();user.setPhone(phone);user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX RandomUtil.randomString((10)));// 2.保存用户save(user);return user;}3.使用redis优化 分析 每个tomcat中都有一份属于自己的session,假设用户第一次访问第一台tomcat并且把自己的信息存放到第一台服务器的session中但是第二次这个用户访问到了第二台tomcat那么在第二台服务器上肯定没有第一台服务器存放的session所以此时 整个登录拦截功能就会出现问题我们能如何解决这个问题呢早期的方案是session拷贝就是说虽然每个tomcat上都有不同的session但是每当任意一台服务器的session修改时都会同步给其他的Tomcat服务器的session这样的话就可以实现session的共享了。问题 1、每台服务器中都有完整的一份session数据服务器压力过大。2、session拷贝数据时可能会出现延迟。 解决redis代替session使用Hash结构存储对象 解决 当注册完成后用户去登录会去校验用户提交的手机号和验证码是否一致如果一致则根据手机号查询用户信息不存在则新建最后将用户数据保存到redis并且生成token作为redis的key当我们校验用户是否登录时会去携带着token进行访问从redis中取出token对应的value判断是否存在这个数据如果没有则拦截如果存在则将其保存到threadLocal中并且放行。采用token作为key 让key具有唯一性这样不同用户储存到redis的key唯一方便携带因为前端请求头中可以携带token hash存储对象 key-map结构如上面图片所示优点也如上图。 代码
Override
public 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.一致根据手机号查询用户 select * from tb_user where phone ?User user query().eq(phone, phone).one();// 5.判断用户是否存在if (user null) {// 6.不存在创建新用户并保存user createUserWithPhone(phone);}// 7.保存用户信息到 redis中// 7.1.随机生成token作为登录令牌String token UUID.randomUUID().toString();// 7.2.将User对象转为HashMap存储UserDTO userDTO BeanUtil.copyProperties(user, UserDTO.class);// (将Map集合中的非String类型转换为String)MapString, Object userMap BeanUtil.beanToMap(userDTO, new HashMap(),CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) - fieldValue.toString()));// 7.3.存储String tokenKey LOGIN_USER_KEY token;stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);// 7.4.设置token有效期设置登录后用户权限有效期stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);// 8.返回tokenreturn Result.ok(token);
}二、登录拦截校验 每个用户其实对应都是去找tomcat线程池中的一个线程来完成工作的 使用完成后再进行回收既然每个请求都是独立的所以在每个用户去访问我们的工程时我们可以使用threadlocal来做到线程隔离每个线程操作自己的一份数据。 温馨小贴士tomcat的运行原理 当用户发起请求时会访问我们像tomcat注册的端口任何程序想要运行都需要有一个线程对当前端口号进行监听tomcat也不例外当监听线程知道用户想要和tomcat连接连接时那会由监听线程创建socket连接socket都是成对出现的用户通过socket像互相传递数据当tomcat端的socket接受到数据后此时监听线程会从tomcat的线程池中取出一个线程执行用户请求在我们的服务部署到tomcat后线程会找到用户想要访问的工程然后用这个线程转发到工程中的controllerservicedao中并且访问对应的DB在用户执行完请求后再统一返回再找到tomcat端的socket再将数据写回到用户端的socket完成请求和响应。
1.流程 如果小伙伴们看过threadLocal的源码你会发现在threadLocal中无论是他的put方法和他的get方法 都是先从获得当前用户的线程然后从线程中取出线程的成员变量map只要线程不一样map就不一样所以可以通过这种方式来做到线程隔离 分析到这里可以考虑一下拦截实现了上面的短信登录中有个token有效期设置登录后用户权限有效期就是让用户登录信息状态存在多久后过期我们上面是从登录的那一刻算30分钟后过期可能需要再次验证登录才能操作一些具有权限的页面。其实按照实际我们应该是访问某个页面的时候就应该重新刷新有效期正常有效期存在不可能是用户在操作过程中突然失去登录信息而是在多久不进行任何操作后失效所有我们需要对拦截器进行优化加入刷新token有效期的功能。 2.代码
拦截器
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.获取请求头中的tokenString token request.getHeader(authorization);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数据转为UserDTOUserDTO userDTO BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);// 6.存在保存用户信息到 ThreadLocalUserHolder.saveUser(userDTO);// 7.刷新token有效期stringRedisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.MINUTES);// 8.放行return true;}Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 移除用户UserHolder.removeUser();}
}
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;}
}让拦截器生效配置类添加 注意设置优先级应该让刷新拦截器最优先。 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(1);// token刷新的拦截器registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns(/**).order(0);}
}总结
以上就是短信登录的详细实现细节满满。