先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注大数据)
正文
return Result.ok();
}
### 2、实现用户登录和注册功能(小优化)
**思路分析:**
* 从session中获取手机号(避免用户获取验证码后修改手机号),如果手机号跟表单中的手机号不一致(即用户修改了手机号),则让用户重新发送验证码;
* 接着从session中获取验证码,如果表单中的验证码和发送的验证码不一致,则让用户重新输入验证码;
* 然后从数据库中查询是否存在该手机号(即该用户是否已经注册过),如果不存在,则把该用户插入到数据库;
* 最后返回用户信息(为了减少Tomcat服务器的内存消耗以及用户信息的安全,会使用UserDTO类来返回用户的部分信息)
---
**代码:**
@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session) {
return userService.login(loginForm, session);
}
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
//校验手机号和验证码
String phone = (String) session.getAttribute("phone");
if (!phone.equals(loginForm.getPhone())) {
return Result.fail("手机号改变,请重新获取验证码");
}
String code = (String) session.getAttribute("code");
String cacheCode = loginForm.getCode();
if (cacheCode == null || !cacheCode.equals(code)) {
return Result.fail("验证码错误");
}
//判断用户是否存在
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
.eq(User::getPhone, phone);
User user = userMapper.selectOne(wrapper);
if (user == null) {
user = createUser(phone);
}
//优化:减少Tomcat内存的使用并隐藏用户的敏感信息
session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));
return Result.ok();
}
/\*\*
* 创建用户
*
* @param phone
* @return
*/
private User createUser(String phone) {
User user = new User();
user.setPhone(phone);
user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(8));
userMapper.insert(user);
return user;
}
@Data
public class UserDTO {
private Long id;
private String nickName;
private String icon;
}
---
### 3、实现登录校验拦截器功能
**思路分析:**
* 为了避免在多个controller层中实现登录校验功能,可以使用拦截器,在访问controller之前,进行登录校验;
* 在后续的业务中,因为要用到用户信息,所以要把拦截器中的用户信息传到controller中,并且,为了确保线程安全问题,可以使用ThreadLocal类。我们可以把拦截到的用户信息保存到ThreadLocal对象中。
* [Java中的ThreadLocal详解]( )
---
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();
}
}
---
**拦截器配置代码:**
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
UserDTO user = (UserDTO) session.getAttribute(“user”);
if (user == null) {
response.setStatus(401);
return false;
}
UserHolder.saveUser(user);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.removeUser();
}
}
@Configuration
public class MyConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns(
“/shop/**”,
“/voucher/**”,
“/shop-type/**”,
“/upload/**”,
“/blog/hot”,
“/user/code”,
“/user/login”
);
}
}
---
**controller层代码:**
@GetMapping("/me")
public Result me() {
UserDTO user = UserHolder.getUser();
return Result.ok(user);
}
---
**最后来看看基于session登录的完整流程:**
![在这里插入图片描述](https://img-blog.csdnimg.cn/6cd294103e754ce4bd4c18eafc10ad95.png)
---
## 二、session共享的问题分析
>
> 为什么使用Redis实现登录功能,而不使用基于Session实现登录功能?考虑到多台Tomcat并不共享session存储空间(虽然多台Tomcat可以对数据进行拷贝,但是不仅会造成内存空间的浪费,而且还会因为存在数据拷贝时间上的延迟,如果在延迟时间内有使用者来访问,依然会出现数据不一致的情况!),当请求切换到不同tomcat服务时会导致数据丢失!所以,我们的解决方案应该满足:数据共享、内存存储、key-value结构。
>
>
>
---
## 三、基于Redis实现短信登录
### 1、实现发送验证码功能
**思路分析:**
* 跟基于session发送验证码不同的是:我们把验证码放到redis数据库中,并设置验证码过期时间;
* 使用字符串数据结构:以手机号为key,验证码为value。
**代码实现:**
@PostMapping("/code")
public Result sendCode(@RequestParam("phone") String phone) {
return userService.generateCode(phone);
}
@Override
public Result generateCode(String phone) {
if (RegexUtils.isPhoneInvalid(phone)) {
return Result.fail("手机号格式错误");
}
String code = RandomUtil.randomNumbers(6);
//设置验证码过期时间为2minutes
redisTemplate.opsForValue().set(LOGIN\_CODE\_KEY + phone, code, LOGIN\_CODE\_TTL, TimeUnit.MINUTES);
log.info("生成的验证码是:{}", code);
return Result.ok();
}
---
### 2、实现用户登录和注册功能
**思路分析:**
* 跟基于session实现用户登录和注册不同的是:我们把用户对象放到redis数据库中并设置有效期;
* 对象存储使用哈希结构,随机生成token,作为登录令牌,并以token作为对象存储的key,对象值为value。
---
**代码实现:**
@Override
public Result login(LoginFormDTO loginForm) {
String phone = loginForm.getPhone();
if (!redisTemplate.hasKey(LOGIN\_CODE\_KEY + phone)) {
return Result.fail("手机号改变,请重新获取验证码");
}
String cacheCode = redisTemplate.opsForValue().get(LOGIN\_CODE\_KEY + phone);
String code = loginForm.getCode();
if (code == null || !code.equals(cacheCode)) {
return Result.fail("验证码错误");
}
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
.eq(User::getPhone, phone);
User user = userMapper.selectOne(wrapper);
if (user == null) {
user = createUser(phone);
}
//保存用户信息到redis中
String token = UUID.randomUUID().toString(true);
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
HashMap<String, String> hashMap = getHashMap(userDTO);
token = LOGIN\_USER\_KEY + token;
redisTemplate.opsForHash().putAll(token, hashMap);
//设置有效期为30分钟
redisTemplate.expire(token, LOGIN\_USER\_TTL, TimeUnit.SECONDS);
return Result.ok(token);
}
/\*\*
* 用hashmap存储userDTO对象
* @param userDTO
* @return
*/
private HashMap<String, String> getHashMap(UserDTO userDTO) {
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put(“id”, userDTO.getId().toString());
hashMap.put(“nickName”, userDTO.getNickName());
hashMap.put(“icon”, userDTO.getIcon());
return hashMap;
}
---
### 3、用拦截器实现用户有效期更新
* 我们在service层设置了用户在不做任何操作的情况下,保存用户信息的有效期为30分钟,但是如果用户进行了某种操作,比如点赞、发布博客等等需要登录后才能完成的操作,就需要重新设置用户信息有效期为30分钟。
---
**代码实现:**
public class LoginInterceptor implements HandlerInterceptor {
private StringRedisTemplate redisTemplate;
//构造器注入
public LoginInterceptor(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public 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中的用户
Map<Object, Object> userMap = redisTemplate.opsForHash().entries(token);
// 判断用户是否存在
if (userMap.isEmpty()) {
response.setStatus(401);
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
psForHash().entries(token);
// 判断用户是否存在
if (userMap.isEmpty()) {
response.setStatus(401);
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)
[外链图片转存中…(img-slSYEWgy-1713322204121)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!