刚入门Java的硕士宝宝,实验室没什么Java项目,看到网上的秒杀项目,跟着学习并且记录一下,可能错误会很多,欢迎大家指正。
文章目录
前言
本文介绍分布式会话模块,具体详细介绍实现用户登录和分布式session。
一、实现登录功能
1.两次MD5加密
这种方式是一种增加密码安全性的常见做法,通过在密码的两次加密过程中引入固定盐值和随机盐值,提高了密码的复杂度,增加了破解的难度。
- 第一次加密(前端):
用户在前端输入密码。
前端使用固定的盐值(固定 salt)对密码进行 MD5 加密,得到第一次加密结果。
这个固定的盐值在前端就已经确定了,通常作为一个字符串硬编码在前端代码中。
- 第二次加密(后端):
前端将第一次加密的结果以及用户输入的用户名等信息发送给后端。
后端根据接收到的用户名等信息,在服务端生成一个随机的盐值(随机 salt)。
后端将第一次加密的结果与随机盐值结合,再进行一次 MD5 加密,得到最终的密码存储结果。
2.参数校验
- 添加依赖:
<!-- validation组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- 自定义手机号码验证规则:
public class IsMobileValidator implements ConstraintValidator<IsMobile,String> {
private boolean required = false;
@Override
public void initialize(IsMobile constraintAnnotation) {
required = constraintAnnotation.required();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (required){
return ValidatorUtil.isMobile(value);
}else {
if (StringUtils.isEmpty(value)){
return true;
}else {
return ValidatorUtil.isMobile(value);
}
}
}
}
- 自定义注解:
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {IsMobileValidator.class})
public @interface IsMobile {
boolean required() default true;
String message() default "手机号码格式错误";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
3.异常处理
在 Spring Boot 中,可以使用全局异常处理器来统一处理系统中出现的异常,以确保异常信息的统一处理和维护。主要的两种全局异常处理方式包括:
①使用 @ControllerAdvice 注解:
创建一个类并使用 @ControllerAdvice 注解标注,该类中定义 @ExceptionHandler 注解的方法来处理特定类型的异常。
这种方式允许您将异常处理逻辑集中在一个地方,并且可以处理多个控制器中抛出的异常。
②实现 ErrorController 接口:
创建一个类实现 Spring Boot 提供的 ErrorController 接口,并实现 getErrorPath() 方法和@RequestMapping 注解的方法来处理异常。
这种方式允许您自定义错误页面或者返回特定的错误信息。
二、分布式Session
在部署多台系统,并使用 Nginx 进行负载均衡时可能出现的用户登录问题。具体原因如下:
- 当部署多台系统时,每台系统都会维护自己的 Session;
- 如果使用 Nginx 进行负载均衡,默认的负载均衡策略是轮询,即将请求按照时间顺序逐一分发到后端应用上;
- 假设用户首先在 Tomcat1 上登录,那么用户信息将存储在 Tomcat1 的 Session 中;
- 过了一段时间后,Nginx 可能会将用户的下一个请求发送到 Tomcat2 上,但此时 Tomcat2 的 Session 中并没有用户信息;
- 因此,用户会被重定向到登录页面,需要重新登录。
解决这个问题的一种方法是实现分布式会话管理,确保用户的会话信息可以在多个 Tomcat 实例之间共享。常见的做法包括:
- 使用 Redis 或 Memcached 等分布式缓存服务来存储会话信息,从而实现跨多个 Tomcat 实例的会话共享;
- 使用 Spring Session 等框架来管理分布式会话,这些框架提供了方便的集成方式,并且可以与 Redis、JDBC 等后端存储进行集成;
- 将会话信息存储在数据库中,并确保所有 Tomcat 实例都能访问同一个数据库,从而实现会话共享。
Redis实现分布式Session
方法一:使用SpringSession实现
添加依赖:
<!-- spring data redis 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- commons-pool2 对象池依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- spring-session 依赖 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
方法二:将用户信息存入Redis
- 添加依赖:
<!-- spring data redis 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- commons-pool2 对象池依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
- Redis配置类:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory
connectionFactory){
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
//key序列器
redisTemplate.setKeySerializer(new StringRedisSerializer());
//value序列器
redisTemplate.setValueSerializer(new
GenericJackson2JsonRedisSerializer());
//Hash类型 key序列器
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//Hash类型 value序列器
redisTemplate.setHashValueSerializer(new
GenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
}
- Json工具类:
public class JsonUtil {
private static ObjectMapper objectMapper = new ObjectMapper();
public static String object2JsonStr(Object obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
public static <T> T jsonStr2Object(String jsonStr, Class<T> clazz) {
try {
return objectMapper.readValue(jsonStr.getBytes("UTF-8"), clazz);
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static <T> List<T> jsonToList(String jsonStr, Class<T> beanType) {
JavaType javaType =
objectMapper.getTypeFactory().constructParametricType(List.class, beanType);
try {
List<T> list = objectMapper.readValue(jsonStr, javaType);
return list;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
总结
以上就是今天要讲的内容,本文简单介绍了分布式会话模块,具体参考 乐字节 Java电商秒杀项目。