pull/3/head
litingting 11 months ago
parent e9c5da4aae
commit 0ef71681c9

@ -8,20 +8,38 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
// 这是一个Spring Boot应用的主启动类使用了多个注解来配置应用的相关特性和功能。
@SpringBootApplication @SpringBootApplication
// @SpringBootApplication注解是一个复合注解它包含了多个用于开启Spring Boot自动配置、组件扫描等功能的注解
// 表明这是一个Spring Boot应用的入口点会自动配置很多默认的Spring组件和行为方便快速搭建项目框架。
@EnableDiscoveryClient @EnableDiscoveryClient
// @EnableDiscoveryClient注解用于启用服务发现功能使得该应用能够注册到服务注册中心如Eureka、Consul等
// 并且可以从注册中心发现其他服务,方便在微服务架构中实现服务之间的相互调用和协作。
@EnableZuulProxy @EnableZuulProxy
@PropertySource(value="classpath:parameter.properties") // @EnableZuulProxy注解用于启用Zuul作为微服务架构中的API网关代理Zuul可以对外部请求进行路由转发、过滤等操作
// 例如根据不同的请求路径将请求转发到相应的后端微服务上,还能实现诸如限流、权限校验等功能。
@PropertySource(value = "classpath:parameter.properties")
// @PropertySource注解用于指定外部属性文件的位置这里表示从类路径下的parameter.properties文件中加载配置属性
// 可以在该文件中定义各种项目中需要用到的配置项如数据库连接信息、服务端口等Spring会读取并注入这些配置到相应的组件中使用。
public class SnailmallApiGatewayApplication { public class SnailmallApiGatewayApplication {
// 应用的主方法是Java应用程序的入口点用于启动Spring Boot应用。
public static void main(String[] args) { public static void main(String[] args) {
// 调用SpringApplication的静态run方法来启动Spring Boot应用传入当前主启动类的Class对象以及命令行参数
// Spring Boot会根据配置自动完成一系列的初始化、组件加载、配置注入等操作然后启动应用并监听相应的端口等待外部请求。
SpringApplication.run(SnailmallApiGatewayApplication.class, args); SpringApplication.run(SnailmallApiGatewayApplication.class, args);
} }
// 使用@Bean注解定义一个Spring Bean该方法返回的对象会被Spring容器管理可在其他组件中通过依赖注入的方式使用。
@Bean @Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() { public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
// 创建并返回一个PropertySourcesPlaceholderConfigurer对象它主要用于处理属性占位符的替换功能
// 在Spring应用中如果配置文件中的属性使用了占位符例如${property.name}这种形式),该配置器会帮助将占位符替换为实际配置的值,
// 确保应用能够正确使用配置文件中的各项配置属性。
return new PropertySourcesPlaceholderConfigurer(); return new PropertySourcesPlaceholderConfigurer();
} }
} }

@ -14,29 +14,52 @@ import javax.annotation.PostConstruct;
* @Date 2019/1/1 15:00 * @Date 2019/1/1 15:00
* @CONTACT 317758022@qq.com * @CONTACT 317758022@qq.com
* @DESC redisredishash * @DESC redisredishash
* JedisPool便JedisPoolRedis
* RedisRedishash
*/ */
@Component @Component
@Slf4j @Slf4j
public class JedisPoolWrapper { public class JedisPoolWrapper {
// 通过Spring的依赖注入获取Parameters对象该对象应该包含了Redis相关的配置参数
@Autowired @Autowired
private Parameters parameters; private Parameters parameters;
// 用于存放JedisPool实例初始化为null后续在初始化方法中进行实例化赋值
private JedisPool jedisPool = null; private JedisPool jedisPool = null;
/**
* @PostConstruct
* JedisPool
*/
@PostConstruct @PostConstruct
public void init(){ public void init() {
try { try {
// 创建JedisPoolConfig对象用于配置JedisPool的各项参数
JedisPoolConfig config = new JedisPoolConfig(); JedisPoolConfig config = new JedisPoolConfig();
// 设置JedisPool中最大连接数参数值从Parameters对象中获取
config.setMaxTotal(parameters.getRedisMaxTotal()); config.setMaxTotal(parameters.getRedisMaxTotal());
// 设置JedisPool中空闲连接的最大数量同样从Parameters对象获取对应参数值
config.setMaxIdle(parameters.getRedisMaxIdle()); config.setMaxIdle(parameters.getRedisMaxIdle());
// 设置获取连接时的最大等待时间单位为毫秒也是从Parameters对象获取对应参数值
config.setMaxWaitMillis(parameters.getRedisMaxWaitMillis()); config.setMaxWaitMillis(parameters.getRedisMaxWaitMillis());
jedisPool = new JedisPool(config,parameters.getRedisHost(),parameters.getRedisPort(),2000,"xxx"); // 创建JedisPool实例传入配置对象、Redis服务器主机地址、端口号、超时时间以及密码等信息
// 这里密码写为"xxx"实际应用中应替换为真实的Redis访问密码
jedisPool = new JedisPool(config, parameters.getRedisHost(), parameters.getRedisPort(), 2000, "xxx");
// 如果初始化成功记录日志信息表示初始化Redis连接池成功
log.info("【初始化redis连接池成功】"); log.info("【初始化redis连接池成功】");
}catch (Exception e){ } catch (Exception e) {
log.error("【初始化redis连接池失败】",e); // 如果在初始化过程中出现异常,记录详细的错误日志,日志内容为"【初始化redis连接池失败】"以及异常信息
log.error("【初始化redis连接池失败】", e);
} }
} }
/**
* JedisPool
* JedisPoolRedis
*
* @return JedisPoolnull
*/
public JedisPool getJedisPool() { public JedisPool getJedisPool() {
return jedisPool; return jedisPool;
} }

@ -11,23 +11,54 @@ import java.util.List;
* @Date 2019/1/1 14:27 * @Date 2019/1/1 14:27
* @CONTACT 317758022@qq.com * @CONTACT 317758022@qq.com
* @DESC * @DESC
* Spring
* 使Lombok@DataGetterSetterToString便访
*/ */
@Component @Component
@Data @Data
public class Parameters { public class Parameters {
/*****redis config start*******/ /*****redis config start*******/
/**
* @Valueapplication.propertiesapplication.yml"redis.host"
* Redis
*/
@Value("${redis.host}") @Value("${redis.host}")
private String redisHost; private String redisHost;
/**
* 使@Value"redis.port"
* Redis
*/
@Value("${redis.port}") @Value("${redis.port}")
private int redisPort; private int redisPort;
/**
* max-idlemaxIdlemax-totalmaxTotal
* "redis.max-idle"Redis
*/
@Value("${redis.max-idle}") @Value("${redis.max-idle}")
private int redisMaxTotal; private int redisMaxTotal;
/**
* @Value"redis.max-total"
* Redis
*/
@Value("${redis.max-total}") @Value("${redis.max-total}")
private int redisMaxIdle; private int redisMaxIdle;
/**
* "redis.max-wait-millis"
* Redis
*/
@Value("${redis.max-wait-millis}") @Value("${redis.max-wait-millis}")
private int redisMaxWaitMillis; private int redisMaxWaitMillis;
/*****redis config end*******/ /*****redis config end*******/
/**
* 使@ValueSpELSpring"security.noneSecurityAdminPaths"
* List<String>
*
*/
@Value("#{'${security.noneSecurityAdminPaths}'.split(',')}") @Value("#{'${security.noneSecurityAdminPaths}'.split(',')}")
private List<String> noneSecurityAdminPaths; private List<String> noneSecurityAdminPaths;
} }

@ -11,22 +11,41 @@ import java.util.Arrays;
/** /**
* *
* C - Cross O - Origin R - Resource S - Sharing * C - Cross O - Origin R - Resource S - Sharing
* Spring使访
*
*/ */
@Configuration @Configuration
public class CorsConfig { public class CorsConfig {
/**
* @BeanBeanSpring
* CorsFilterBeanSpring
*
*/
@Bean @Bean
public CorsFilter corsFilter() { public CorsFilter corsFilter() {
// 创建一个基于URL的跨域配置源对象用于根据不同的URL路径来应用不同的跨域配置规则
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 创建一个CorsConfiguration对象用于具体设置跨域相关的各种配置参数
final CorsConfiguration config = new CorsConfiguration(); final CorsConfiguration config = new CorsConfiguration();
// 设置是否允许携带凭证例如Cookie等进行跨域请求这里设置为true表示允许
config.setAllowCredentials(true); config.setAllowCredentials(true);
config.setAllowedOrigins(Arrays.asList("*")); //http:www.a.com // 设置允许跨域访问的源Origin列表这里使用Arrays.asList("*")表示允许所有来源的请求跨域访问,
// 在实际应用中更安全的做法是明确指定具体的源地址例如http://www.a.com这种形式
config.setAllowedOrigins(Arrays.asList("*"));
// 设置允许跨域请求包含的头部信息列表使用Arrays.asList("*")表示允许所有头部信息,
// 但在更严格的安全场景下,应该明确列出允许的头部字段
config.setAllowedHeaders(Arrays.asList("*")); config.setAllowedHeaders(Arrays.asList("*"));
// 设置允许的跨域请求方法列表如GET、POST等这里使用Arrays.asList("*")表示允许所有请求方法跨域访问
config.setAllowedMethods(Arrays.asList("*")); config.setAllowedMethods(Arrays.asList("*"));
// 设置浏览器对预检请求OPTIONS请求的缓存时间单位为秒这里设置为300秒
// 即在这个时间范围内,对于相同源的相同请求,浏览器无需再次发送预检请求
config.setMaxAge(300l); config.setMaxAge(300l);
// 将上述配置应用到所有路径("/**"表示匹配所有路径)上,这样所有接口都会应用此跨域配置
source.registerCorsConfiguration("/**", config); source.registerCorsConfiguration("/**", config);
// 返回创建好的CorsFilter对象该过滤器会在Spring处理Web请求时根据配置拦截并处理跨域相关情况
return new CorsFilter(source); return new CorsFilter(source);
} }
} }

@ -5,42 +5,72 @@ package com.njupt.swg.constants;
* @Date 2019/1/1 13:19 * @Date 2019/1/1 13:19
* @CONTACT 317758022@qq.com * @CONTACT 317758022@qq.com
* @DESC * @DESC
* 便
*/ */
public class Constants { public class Constants {
/**自定义状态码 start**/
/**
* start
* 便
*/
// 表示请求成功的状态码对应HTTP状态码中的200表示操作成功完成
public static final int RESP_STATUS_OK = 200; public static final int RESP_STATUS_OK = 200;
// 表示未授权的状态码对应HTTP状态码中的401通常用于用户未通过认证或授权时返回的状态
public static final int RESP_STATUS_NOAUTH = 401; public static final int RESP_STATUS_NOAUTH = 401;
// 表示服务器内部错误的状态码对应HTTP状态码中的500用于在服务器端出现异常等内部问题时返回该状态告知客户端
public static final int RESP_STATUS_INTERNAL_ERROR = 500; public static final int RESP_STATUS_INTERNAL_ERROR = 500;
// 表示请求参数错误的状态码对应HTTP状态码中的400当客户端发送的请求参数不符合要求时返回此状态
public static final int RESP_STATUS_BADREQUEST = 400; public static final int RESP_STATUS_BADREQUEST = 400;
/**
* end
*/
/**自定义状态码 end**/ /**
* *redis userkey
/***redis user相关的key以这个打头**/ * Rediskey便
*/
public static final String TOKEN_PREFIX = "user_"; public static final String TOKEN_PREFIX = "user_";
/** /**
* redis * redis
* Redis便
*/ */
public interface RedisCacheExtime{ public interface RedisCacheExtime{
int REDIS_SESSION_EXTIME = 60* 30;//30分钟 // 定义用户登录信息在Redis中的过期时间以秒为单位这里设置为30分钟60秒 * 30方便后续统一管理和修改该时间设置
int REDIS_SESSION_EXTIME = 60 * 30; //30分钟
} }
/** 用户注册判断重复的参数类型 start **/ /**
* start
* 便使
*/
// 表示注册时判断邮箱是否重复的参数类型对应的常量字符串,用于标识邮箱字段
public static final String EMAIL = "email"; public static final String EMAIL = "email";
// 表示注册时判断用户名是否重复的参数类型对应的常量字符串,用于标识用户名字段
public static final String USERNAME = "username"; public static final String USERNAME = "username";
/** 用户注册判断重复的参数类型 end **/ /**
* end
*/
/** 用户角色 **/ /**
*
* 便使
*/
public interface Role{ public interface Role{
int ROLE_CUSTOME = 0;//普通用户 // 表示普通用户角色对应的整数值赋值为0方便在代码中通过该值来判断用户是否为普通用户角色
int ROLE_ADMIN = 1;//管理员用户 int ROLE_CUSTOME = 0; //普通用户
// 表示管理员用户角色对应的整数值赋值为1用于判断用户是否为管理员角色
int ROLE_ADMIN = 1; //管理员用户
} }
/**用户注册分布式锁路径***/ /**
*
* 使便
*/
public static final String USER_REGISTER_DISTRIBUTE_LOCK_PATH = "/user_reg"; public static final String USER_REGISTER_DISTRIBUTE_LOCK_PATH = "/user_reg";
} }

@ -11,17 +11,30 @@ import org.springframework.web.bind.annotation.RestController;
* @Date 2019/1/2 21:21 * @Date 2019/1/2 21:21
* @CONTACT 317758022@qq.com * @CONTACT 317758022@qq.com
* @DESC * @DESC
* Spring MVCSpring BootErrorController
*
*/ */
@RestController @RestController
public class ErrorHandleController implements ErrorController { public class ErrorHandleController implements ErrorController {
/**
* ErrorController
* "/error""/error"
*/
@Override @Override
public String getErrorPath() { public String getErrorPath() {
return "/error"; return "/error";
} }
/**
* 使@RequestMapping"/error"
* "/error"
* ServerResponseServerResponse
* ResponseEnum
*/
@RequestMapping("/error") @RequestMapping("/error")
public ServerResponse error() { public ServerResponse error() {
return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登陆或者权限不足"); return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登陆或者权限不足");
} }
} }

@ -13,31 +13,45 @@ import java.util.Date;
* @Date 2018/12/31 21:01 * @Date 2018/12/31 21:01
* @CONTACT 317758022@qq.com * @CONTACT 317758022@qq.com
* @DESC * @DESC
* 使Lombok
* GetterSetterToStringJava
*/ */
@Data @Data
// 使用@Data注解由Lombok自动为类中的所有非final字段生成Getter、Setter方法同时还生成了equals、hashCode和toString方法方便对对象属性的访问和操作。
@NoArgsConstructor @NoArgsConstructor
// 使用@NoArgsConstructor注解由Lombok自动生成一个无参构造函数方便在某些情况下如通过反射等方式创建对象实例对对象进行初始化。
@AllArgsConstructor @AllArgsConstructor
// 使用@AllArgsConstructor注解由Lombok自动生成一个包含所有参数的构造函数可用于在创建对象时一次性传入所有必要的属性值来初始化对象。
@ToString @ToString
// 使用@ToString注解由Lombok自动为该类生成一个toString方法便于在调试等场景下直观地查看对象的属性值信息。
public class User implements Serializable { public class User implements Serializable {
// 用户的唯一标识符,通常对应数据库表中的主键,用于区分不同的用户记录,类型为整数类型。
private Integer id; private Integer id;
// 用户的用户名,用于用户登录等场景下的身份标识,是一个字符串类型的属性。
private String username; private String username;
// 用户的登录密码,存储用户登录时需要验证的密码信息,为字符串类型,通常会进行加密存储以保证安全性。
private String password; private String password;
// 用户的电子邮箱地址,可用于找回密码、接收系统通知等功能,是字符串类型的属性。
private String email; private String email;
// 用户的电话号码,可能用于联系用户、短信验证等相关业务场景,同样为字符串类型。
private String phone; private String phone;
// 用户设置的密保问题,用于在忘记密码等情况下辅助验证用户身份,属于字符串类型的属性。
private String question; private String question;
// 用户对密保问题设置的答案与question属性对应用于验证用户身份也是字符串类型。
private String answer; private String answer;
//角色0-管理员,1-普通用户 // 用户角色通过整数值来表示不同的角色类型0代表管理员1代表普通用户用于权限控制等相关业务逻辑。
private Integer role; private Integer role;
// 用户账号创建的时间记录用户首次注册创建账号的具体日期和时间类型为Java中的Date类型。
private Date createTime; private Date createTime;
// 用户信息最后更新的时间每次用户修改自身信息等操作后会更新该时间同样是Date类型的属性。
private Date updateTime; private Date updateTime;
} }

@ -8,18 +8,33 @@ import lombok.Getter;
* @Date 2019/1/1 13:18 * @Date 2019/1/1 13:18
* @CONTACT 317758022@qq.com * @CONTACT 317758022@qq.com
* @DESC * @DESC
* JavaRuntimeException
* 便
*/ */
@Getter @Getter
public class SnailmallException extends RuntimeException{ // 使用Lombok的@Getter注解自动为类中的私有属性生成对应的Getter方法使得外部可以获取这些属性的值。
public class SnailmallException extends RuntimeException {
// 用于存储异常对应的状态码初始化为ResponseEnum.ERROR.getCode(),即默认的错误状态码,后续可通过构造函数进行修改。
private int exceptionStatus = ResponseEnum.ERROR.getCode(); private int exceptionStatus = ResponseEnum.ERROR.getCode();
public SnailmallException(String msg){ /**
* msg
* RuntimeException便
* @param msg
*/
public SnailmallException(String msg) {
super(msg); super(msg);
} }
public SnailmallException(int code,String msg){ /**
* codemsg
* exceptionStatus
* 便
* @param code
* @param msg msg
*/
public SnailmallException(int code, String msg) {
super(msg); super(msg);
exceptionStatus = code; exceptionStatus = code;
} }
} }

@ -27,26 +27,43 @@ import static org.springframework.cloud.netflix.zuul.filters.support.FilterConst
* @Date 2019/1/3 10:21 * @Date 2019/1/3 10:21
* @CONTACT 317758022@qq.com * @CONTACT 317758022@qq.com
* @DESC controller * @DESC controller
* userID,controller * userIDcontroller
* ZuulFilterZuul
*
*/ */
@Slf4j @Slf4j
@Component @Component
public class AdminUserFilter extends ZuulFilter { public class AdminUserFilter extends ZuulFilter {
// 通过依赖注入获取CommonCacheUtil实例用于后续从缓存Redis中获取用户相关信息
@Autowired @Autowired
private CommonCacheUtil commonCacheUtil; private CommonCacheUtil commonCacheUtil;
// 通过依赖注入获取Parameters实例该实例可能包含了一些系统配置参数比如不需要安全校验的路径等信息
@Autowired @Autowired
private Parameters parameters; private Parameters parameters;
/**
*
* Spring Cloud ZuulPRE_TYPE
*/
@Override @Override
public String filterType() { public String filterType() {
return PRE_TYPE; return PRE_TYPE;
} }
/**
*
* PRE_DECORATION_FILTER_ORDER1使
*/
@Override @Override
public int filterOrder() { public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER-1; return PRE_DECORATION_FILTER_ORDER - 1;
} }
/**
* URL
* HttpServletRequestURL
* URLfalsetrue
*/
@Override @Override
public boolean shouldFilter() { public boolean shouldFilter() {
RequestContext requestContext = RequestContext.getCurrentContext(); RequestContext requestContext = RequestContext.getCurrentContext();
@ -54,63 +71,68 @@ public class AdminUserFilter extends ZuulFilter {
//获取当前请求的url //获取当前请求的url
String url = request.getRequestURI(); String url = request.getRequestURI();
//前端的路径不在这里校验,直接放过 //前端的路径不在这里校验,直接放过
if (!url.contains("manage")){ if (!url.contains("manage")) {
log.info("【{}不需要进行权限校验】",url); log.info("【{}不需要进行权限校验】", url);
return false; return false;
} }
if(url.contains("upload")){ if (url.contains("upload")) {
log.info("【{}不需要进行权限校验】",url); log.info("【{}不需要进行权限校验】", url);
return false; return false;
} }
//从配置文件获取所有门户需要校验的路径 //从配置文件获取所有门户需要校验的路径
// String[] passUrls = parameters.getNoneSecurityAdminPaths().toArray(new String[parameters.getNoneSecurityAdminPaths().size()]); // String[] passUrls = parameters.getNoneSecurityAdminPaths().toArray(new String[parameters.getNoneSecurityAdminPaths().size()]);
// for(String str:passUrls){ // for (String str : passUrls) {
// //指定的路径比较特殊,也不在这里校验 // //指定的路径比较特殊,也不在这里校验
// if(url.contains("manage") && url.contains(str)){ // if (url.contains("manage") && url.contains(str)) {
// log.info("【{}不需要进行权限校验】",url); // log.info("【{}不需要进行权限校验】", url);
// return false; // return false;
// } // }
// } // }
log.info("【{}----需要进行权限校验,必须是管理员身份才可以进入】",url); log.info("【{}----需要进行权限校验,必须是管理员身份才可以进入】", url);
return true; return true;
} }
/**
* shouldFiltertrue
*
*
*/
@Override @Override
public ServerResponse run() throws ZuulException { public ServerResponse run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext(); RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest(); HttpServletRequest request = requestContext.getRequest();
//校验是否为管理员身份 //校验是否为管理员身份
String loginToken = CookieUtil.readLoginToken(request); String loginToken = CookieUtil.readLoginToken(request);
log.info("【获取cookie----{}】",loginToken); log.info("【获取cookie----{}】", loginToken);
if(StringUtils.isEmpty(loginToken)){ if (StringUtils.isEmpty(loginToken)) {
// // 过滤该请求,不对其进行路由 // // 过滤该请求,不对其进行路由
// requestContext.setSendZuulResponse(false); // requestContext.setSendZuulResponse(false);
// //返回错误代码 // //返回错误代码
// requestContext.setResponseStatusCode(Constants.RESP_STATUS_NOAUTH); // requestContext.setResponseStatusCode(Constants.RESP_STATUS_NOAUTH);
// return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); // return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息");
this.returnMsg(requestContext); this.returnMsg(requestContext);
return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登录,无法获取当前用户信息");
//throw new SnailmallException("用户未登录,无法获取当前用户信息"); //throw new SnailmallException("用户未登录,无法获取当前用户信息");
} }
//2.从redis中获取用户信息 //2.从redis中获取用户信息
String userStr = commonCacheUtil.getCacheValue(loginToken); String userStr = commonCacheUtil.getCacheValue(loginToken);
log.info("【从redis中获取用户信息:{}】",userStr); log.info("【从redis中获取用户信息:{}】", userStr);
if(userStr == null){ if (userStr == null) {
// // 过滤该请求,不对其进行路由 // // 过滤该请求,不对其进行路由
// requestContext.setSendZuulResponse(false); // requestContext.setSendZuulResponse(false);
// //返回错误代码 // //返回错误代码
// requestContext.setResponseStatusCode(Constants.RESP_STATUS_NOAUTH); // requestContext.setResponseStatusCode(Constants.RESP_STATUS_NOAUTH);
// return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); //SnailmallException(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); // return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); //SnailmallException(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息");
this.returnMsg(requestContext); this.returnMsg(requestContext);
return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登录,无法获取当前用户信息");
} }
String url = request.getRequestURI(); String url = request.getRequestURI();
log.info("【获取当前url:{}】",url); log.info("【获取当前url:{}】", url);
if(url.contains("manage")){ if (url.contains("manage")) {
log.info("【来到了管理后台,需要校验权限】"); log.info("【来到了管理后台,需要校验权限】");
User currentUser = JsonUtil.Str2Obj(userStr,User.class); User currentUser = JsonUtil.Str2Obj(userStr, User.class);
log.info("【当前登陆的用户为:{}】",currentUser); log.info("【当前登陆的用户为:{}】", currentUser);
if(!currentUser.getRole().equals(Constants.Role.ROLE_ADMIN)){ if (!currentUser.getRole().equals(Constants.Role.ROLE_ADMIN)) {
//不是管理员报错 //不是管理员报错
log.error("【当前登陆用户不是管理员身份】"); log.error("【当前登陆用户不是管理员身份】");
// 过滤该请求,不对其进行路由 // 过滤该请求,不对其进行路由
@ -119,7 +141,7 @@ public class AdminUserFilter extends ZuulFilter {
// requestContext.setResponseStatusCode(Constants.RESP_STATUS_NOAUTH); // requestContext.setResponseStatusCode(Constants.RESP_STATUS_NOAUTH);
// return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); // return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息");
this.returnMsg(requestContext); this.returnMsg(requestContext);
return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"); return ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登录,无法获取当前用户信息");
//throw new SnailmallException("用户权限不够"); //throw new SnailmallException("用户权限不够");
} }
} }
@ -127,10 +149,14 @@ public class AdminUserFilter extends ZuulFilter {
} }
//返回没权限消息 //返回没权限消息
private void returnMsg(RequestContext ctx){ private void returnMsg(RequestContext ctx) {
// 设置响应的内容类型为JSON格式字符编码为utf-8确保返回的错误信息能被正确解析
ctx.getResponse().setContentType("application/json; charset=utf-8"); ctx.getResponse().setContentType("application/json; charset=utf-8");
ctx.setSendZuulResponse(false); //令zuul过滤该请求不对其进行路由 // 令zuul过滤该请求不对其进行路由即阻止请求继续往后端服务转发
ctx.setSendZuulResponse(false);
// 设置响应的状态码这里设置为Constants.RESP_STATUS_OK可能需要根据实际情况确认是否合适一般权限相关错误可以考虑用对应的错误状态码
ctx.setResponseStatusCode(Constants.RESP_STATUS_OK); ctx.setResponseStatusCode(Constants.RESP_STATUS_OK);
ctx.setResponseBody(JsonUtil.obj2String(ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(),"用户未登录,无法获取当前用户信息"))); // 将错误提示信息转换为JSON字符串并设置为响应体的内容以便客户端能获取到具体的错误提示信息
ctx.setResponseBody(JsonUtil.obj2String(ServerResponse.createByErrorCodeMessage(ResponseEnum.NEED_LOGIN.getCode(), "用户未登录,无法获取当前用户信息")));
} }
} }

@ -16,36 +16,63 @@ import static org.springframework.cloud.netflix.zuul.filters.support.FilterConst
* @Date 2019/1/3 11:21 * @Date 2019/1/3 11:21
* @CONTACT 317758022@qq.com * @CONTACT 317758022@qq.com
* @DESC * @DESC
* ZuulFilterZuul
*
*/ */
@Component @Component
public class RateLimitFilter extends ZuulFilter { public class RateLimitFilter extends ZuulFilter {
//放100个令牌 //放100个令牌
// 使用RateLimiter类来自Google Guava库创建一个令牌桶限流器设置初始令牌数量为100个
// 意味着令牌桶一开始有100个可用令牌后续请求需要获取令牌才能通过该过滤器继续路由。
private static final RateLimiter RATE_LIMITER = RateLimiter.create(100); private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);
/**
*
* Spring Cloud ZuulPRE_TYPE
*/
@Override @Override
public String filterType() { public String filterType() {
return PRE_TYPE; return PRE_TYPE;
} }
/**
*
* SERVLET_DETECTION_FILTER_ORDER1使
*/
@Override @Override
public int filterOrder() { public int filterOrder() {
return SERVLET_DETECTION_FILTER_ORDER - 1; return SERVLET_DETECTION_FILTER_ORDER - 1;
} }
/**
* true
*
*/
@Override @Override
public boolean shouldFilter() { public boolean shouldFilter() {
return true; return true;
} }
/**
* shouldFiltertrue
*
*
*/
@Override @Override
public Object run() throws ZuulException { public Object run() throws ZuulException {
RequestContext context = RequestContext.getCurrentContext(); RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest(); HttpServletRequest request = context.getRequest();
if(!RATE_LIMITER.tryAcquire()){ // 使用tryAcquire方法尝试从令牌桶中获取一个令牌该方法会立即返回获取结果
// 如果获取成功即令牌桶中有可用令牌则返回true请求可以继续往后执行如果获取失败令牌桶中无可用令牌则返回false。
if (!RATE_LIMITER.tryAcquire()) {
//没有取到一个令牌的话,可以这样返回信息给前端 //没有取到一个令牌的话,可以这样返回信息给前端
context.set("状态码",401); // 在请求上下文中设置一个名为"状态码"的属性值为401表示未授权状态这里只是简单示例设置状态码实际应用可能需遵循规范的状态码使用方式
context.set("error.message","用户没有获取到令牌"); // 用于后续可能的错误处理或者向客户端返回相应的提示信息,告知客户端请求因未获取到令牌而被限制。
context.set("状态码", 401);
// 在请求上下文中设置一个名为"error.message"的属性,值为"用户没有获取到令牌"
// 用于更详细地向客户端说明请求被限制的原因,方便客户端知晓具体的错误情况。
context.set("error.message", "用户没有获取到令牌");
} }
return null; return null;
} }

@ -7,18 +7,33 @@ import lombok.Getter;
* @Date 2018/12/31 20:15 * @Date 2018/12/31 20:15
* @CONTACT 317758022@qq.com * @CONTACT 317758022@qq.com
* @DESC * @DESC
*
* 便使
*/ */
@Getter @Getter
// 使用Lombok的@Getter注解会自动为枚举中的code和desc属性生成对应的Getter方法便于外部获取这些属性的值。
public enum ResponseEnum { public enum ResponseEnum {
SUCCESS(0,"SUCCESS"), // 定义一个名为SUCCESS的枚举常量表示操作成功的响应状态其对应的状态码为0描述信息为"SUCCESS"
ERROR(1,"ERROR"), // 可用于在返回响应给客户端等场景下表示请求处理成功的情况。
ILLEGAL_ARGUMENTS(2,"ILLEGAL_ARGUMENTS"), SUCCESS(0, "SUCCESS"),
NEED_LOGIN(10,"NEED_LOGIN"); // 定义一个名为ERROR的枚举常量表示操作出现错误的响应状态状态码为1描述信息为"ERROR"
// 当系统出现一般性错误时可使用该枚举值来构建相应的错误响应返回给客户端。
ERROR(1, "ERROR"),
// 定义一个名为ILLEGAL_ARGUMENTS的枚举常量用于表示请求参数不合法的响应状态状态码为2描述信息为"ILLEGAL_ARGUMENTS"
// 当客户端发送的请求参数不符合要求时,可以用此枚举值来构造响应告知客户端参数非法情况。
ILLEGAL_ARGUMENTS(2, "ILLEGAL_ARGUMENTS"),
// 定义一个名为NEED_LOGIN的枚举常量用于表示需要用户登录的响应状态状态码为10描述信息为"NEED_LOGIN"
// 当客户端发起需要登录才能访问的请求但未登录时,可通过该枚举值构建响应提示客户端进行登录操作。
NEED_LOGIN(10, "NEED_LOGIN");
// 用于存储响应状态对应的代码,不同的枚举常量有不同的整数值,以此区分不同的响应情况。
private int code; private int code;
// 用于存储响应状态对应的描述信息,以字符串形式展示具体的状态含义,方便直观理解每个枚举常量所代表的情况。
private String desc; private String desc;
ResponseEnum(int code,String desc){ // 枚举类型的构造函数用于初始化每个枚举常量对应的code和desc属性值
// 在定义枚举常量时传入相应的代码和描述信息,以此来完成每个枚举常量的具体状态设置。
ResponseEnum(int code, String desc) {
this.code = code; this.code = code;
this.desc = desc; this.desc = desc;
} }

@ -11,65 +11,110 @@ import java.io.Serializable;
* @Date 2018/12/31 20:11 * @Date 2018/12/31 20:11
* @CONTACT 317758022@qq.com * @CONTACT 317758022@qq.com
* @DESC * @DESC
*
* 便
*/ */
@Getter @Getter
// 使用Lombok的@Getter注解自动为类中的私有属性status、msg、data生成对应的Getter方法便于外部获取这些属性的值。
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
// 使用@JsonSerialize注解并指定Inclusion.NON_NULL配置用于在将对象转换为JSON格式时忽略值为null的属性
// 这样在返回给客户端的JSON数据中就不会包含那些没有实际值的属性减少数据传输量并使数据结构更简洁。
public class ServerResponse<T> implements Serializable { public class ServerResponse<T> implements Serializable {
// 用于存储响应的状态码,通过不同的状态码值来表示请求处理的结果情况,如成功、失败、需要登录等不同状态。
private int status; private int status;
// 用于存储响应的提示消息,以字符串形式向客户端传达一些与响应相关的额外信息,比如成功时的提示、失败原因等。
private String msg; private String msg;
// 用于存储响应中具体的数据内容,该数据类型通过泛型参数<T>来灵活指定,根据不同的业务场景可以是各种类型的数据(如对象、列表等)。
private T data; private T data;
private ServerResponse(int status){ // 私有构造函数接收一个状态码参数用于创建只包含状态码的ServerResponse对象实例
// 通常在一些内部初始化或者特定场景下使用,其他更完整的构造函数会基于此进一步设置更多属性值。
private ServerResponse(int status) {
this.status = status; this.status = status;
} }
private ServerResponse(int status,String msg){
// 私有构造函数接收状态码和提示消息两个参数用于创建包含状态码和提示消息的ServerResponse对象实例
// 可用于在需要返回带有特定提示信息的响应情况时进行对象初始化,比如返回错误提示消息等场景。
private ServerResponse(int status, String msg) {
this.status = status; this.status = status;
this.msg = msg; this.msg = msg;
} }
private ServerResponse(int status,T data){
// 私有构造函数接收状态码和具体数据两个参数用于创建包含状态码和具体业务数据的ServerResponse对象实例
// 当有需要返回成功处理后的数据给客户端时,可以使用此构造函数来初始化响应对象。
private ServerResponse(int status, T data) {
this.status = status; this.status = status;
this.data = data; this.data = data;
} }
private ServerResponse(int status,String msg,T data){
// 私有构造函数接收状态码、提示消息以及具体数据三个参数用于创建一个完整包含状态码、提示消息和业务数据的ServerResponse对象实例
// 可以在需要同时返回详细提示信息和数据的场景下使用此构造函数进行对象初始化。
private ServerResponse(int status, String msg, T data) {
this.status = status; this.status = status;
this.msg = msg; this.msg = msg;
this.data = data; this.data = data;
} }
/**
* @JsonIgnore使JSON
* ResponseEnum.SUCCESS.getCode()
* truefalse
*/
@JsonIgnore @JsonIgnore
public boolean isSuccess(){ public boolean isSuccess() {
return this.status == ResponseEnum.SUCCESS.getCode(); return this.status == ResponseEnum.SUCCESS.getCode();
} }
/** /**
* *
* ServerResponse便
*
*/ */
public static <T>ServerResponse<T> createBySuccess(){ // 创建一个表示成功的ServerResponse对象实例状态码使用ResponseEnum.SUCCESS中定义的成功状态码提示消息也使用ResponseEnum.SUCCESS中定义的描述信息
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),ResponseEnum.SUCCESS.getDesc()); // 适用于简单的成功情况,不需要额外传递具体业务数据和自定义提示消息的场景。
public static <T> ServerResponse<T> createBySuccess() {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), ResponseEnum.SUCCESS.getDesc());
} }
public static <T>ServerResponse<T> createBySuccessMessage(String message){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),message); // 创建一个表示成功的ServerResponse对象实例状态码使用ResponseEnum.SUCCESS中定义的成功状态码提示消息使用传入的自定义参数message
// 适用于需要返回成功状态但想自定义提示消息给客户端的场景,比如提示操作成功的具体说明等情况。
public static <T> ServerResponse<T> createBySuccessMessage(String message) {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), message);
} }
public static <T>ServerResponse<T> createBySuccess(T data){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),data); // 创建一个表示成功的ServerResponse对象实例状态码使用ResponseEnum.SUCCESS中定义的成功状态码具体业务数据使用传入的参数data
// 适用于有具体业务数据需要返回给客户端以表示操作成功且携带对应数据的场景,比如查询数据成功后返回查询到的数据列表等情况。
public static <T> ServerResponse<T> createBySuccess(T data) {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), data);
} }
public static <T>ServerResponse<T> createBySuccess(String message,T data){
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(),message,data); // 创建一个表示成功的ServerResponse对象实例状态码使用ResponseEnum.SUCCESS中定义的成功状态码提示消息使用传入的参数message具体业务数据使用传入的参数data
// 适用于既需要返回自定义的提示消息又要携带具体业务数据来表示成功情况的场景,更加灵活地满足不同业务需求下成功响应的构建。
public static <T> ServerResponse<T> createBySuccess(String message, T data) {
return new ServerResponse<>(ResponseEnum.SUCCESS.getCode(), message, data);
} }
/** /**
* *
* ServerResponse便
*
*/ */
public static <T>ServerResponse<T> createByError(){ // 创建一个表示失败的ServerResponse对象实例状态码使用ResponseEnum.ERROR中定义的表示错误的状态码提示消息也使用ResponseEnum.ERROR中定义的描述信息
return new ServerResponse<>(ResponseEnum.ERROR.getCode(),ResponseEnum.ERROR.getDesc()); // 适用于一般性的错误情况,返回默认的错误提示给客户端表示操作出现问题的场景。
} public static <T> ServerResponse<T> createByError() {
public static <T>ServerResponse<T> createByErrorMessage(String msg){ return new ServerResponse<>(ResponseEnum.ERROR.getCode(), ResponseEnum.ERROR.getDesc());
return new ServerResponse<>(ResponseEnum.ERROR.getCode(),msg);
}
public static <T>ServerResponse<T> createByErrorCodeMessage(int code,String msg){
return new ServerResponse<>(code,msg);
} }
// 创建一个表示失败的ServerResponse对象实例状态码使用ResponseEnum.ERROR中定义的表示错误的状态码提示消息使用传入的自定义参数msg
// 适用于需要返回特定错误提示消息给客户端以说明具体错误原因的场景,比如参数错误、业务规则不满足等各种导致失败的具体情况说明。
public static <T> ServerResponse<T> createByErrorMessage(String msg) {
return new ServerResponse<>(ResponseEnum.ERROR.getCode(), msg);
}
// 创建一个表示失败的ServerResponse对象实例状态码使用传入的自定义参数code通常是根据具体错误类型定义的不同状态码提示消息使用传入的参数msg
// 适用于需要根据不同错误类型返回对应状态码和具体错误提示消息的场景,更加灵活准确地向客户端反馈错误情况,比如不同业务模块下的不同错误状态反馈等。
public static <T> ServerResponse<T> createByErrorCodeMessage(int code, String msg) {
return new ServerResponse<>(code, msg);
}
} }

@ -9,40 +9,59 @@ import javax.servlet.http.HttpServletResponse;
/** /**
* cookie * cookie
* CookieWebCookie
* 便Cookie
*/ */
@Slf4j @Slf4j
public class CookieUtil { public class CookieUtil {
// 定义Cookie的域名用于指定该Cookie在哪个域名下有效这里设置为"oursnail.cn"表示该Cookie在该域名及其子域名下都可以被识别和使用。
private final static String COOKIE_DOMAIN = "oursnail.cn"; private final static String COOKIE_DOMAIN = "oursnail.cn";
// 定义Cookie的名称用于唯一标识该Cookie这里设置为"snailmall_login_token",通常用于存储用户登录相关的标识信息(如登录令牌等)。
private final static String COOKIE_NAME = "snailmall_login_token"; private final static String COOKIE_NAME = "snailmall_login_token";
/** /**
* cookie * cookie
* @param response * Cookie便
* @param token * @param response HttpServletResponseCookie
* @param token CookieCOOKIE_NAMECookie
*/ */
public static void writeLoginToken(HttpServletResponse response,String token){ public static void writeLoginToken(HttpServletResponse response, String token) {
Cookie ck = new Cookie(COOKIE_NAME,token); // 创建一个新的Cookie对象指定Cookie的名称使用预先定义的COOKIE_NAME和要存储的值传入的token参数
Cookie ck = new Cookie(COOKIE_NAME, token);
// 设置Cookie的域名使其在指定的域名COOKIE_DOMAIN及其子域名下有效确保在相应的网站范围内能正确识别该Cookie。
ck.setDomain(COOKIE_DOMAIN); ck.setDomain(COOKIE_DOMAIN);
ck.setPath("/");//设值在根目录 // 设置Cookie的路径为根目录"/"表示该Cookie在整个网站的所有页面路径下都可以被访问到即全站有效。
ck.setHttpOnly(true);//不允许通过脚本访问cookie,避免脚本攻击 ck.setPath("/"); //设值在根目录
ck.setMaxAge(60*60*24*365);//一年,-1表示永久,单位是秒maxage不设置的话cookie就不会写入硬盘只会写在内存只在当前页面有效 // 设置Cookie为HttpOnly属性为true这样可以防止通过客户端脚本如JavaScript访问该Cookie有效避免脚本攻击提高安全性。
log.info("write cookieName:{},cookieValue:{}",ck.getName(),ck.getValue()); ck.setHttpOnly(true); //不允许通过脚本访问cookie,避免脚本攻击
// 设置Cookie的最大存活时间这里设置为一年60秒 * 60分钟 * 24小时 * 365天单位是秒
// 如果设置为-1则表示永久有效若不设置该属性maxage不设置的话Cookie就不会写入硬盘只会写在内存只在当前页面有效。
ck.setMaxAge(60 * 60 * 24 * 365); //一年,-1表示永久,单位是秒maxage不设置的话cookie就不会写入硬盘只会写在内存只在当前页面有效
// 记录日志输出要写入的Cookie的名称和值方便在调试等场景下查看具体的Cookie写入情况。
log.info("write cookieName:{},cookieValue:{}", ck.getName(), ck.getValue());
// 将创建并配置好的Cookie添加到响应中这样客户端收到响应后就会将该Cookie保存下来以便后续请求时携带该Cookie信息。
response.addCookie(ck); response.addCookie(ck);
} }
/** /**
* cookie * cookie
* @param request * CookieCOOKIE_NAMECookie
* @return * null
* @param request HttpServletRequestCookie
* @return Cookienull
*/ */
public static String readLoginToken(HttpServletRequest request){ public static String readLoginToken(HttpServletRequest request) {
// 从请求中获取所有的Cookie数组客户端发送请求时会携带之前服务器写入的Cookie信息这里获取到这些Cookie以便后续查找特定的Cookie。
Cookie[] cks = request.getCookies(); Cookie[] cks = request.getCookies();
if(cks != null){ if (cks!= null) {
for(Cookie ck:cks){ for (Cookie ck : cks) {
log.info("cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue()); // 记录每个Cookie的名称和值的日志信息方便在调试等场景下查看请求中携带的Cookie情况。
if(StringUtils.equals(ck.getName(),COOKIE_NAME)){ log.info("cookieName:{},cookieBValue:{}", ck.getName(), ck.getValue());
log.info("return cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue()); // 通过比较Cookie的名称是否与预先定义的登录相关Cookie名称COOKIE_NAME相等来判断是否是我们要找的登录Cookie。
if (StringUtils.equals(ck.getName(), COOKIE_NAME)) {
// 如果找到对应的登录Cookie记录其名称和值的日志信息并返回该Cookie存储的值通常是登录令牌等关键信息
log.info("return cookieName:{},cookieBValue:{}", ck.getName(), ck.getValue());
return ck.getValue(); return ck.getValue();
} }
} }
@ -52,18 +71,27 @@ public class CookieUtil {
/** /**
* *
* @param request * Cookie使Cookie
* @param response *
* @param request HttpServletRequestCookie便Cookie
* @param response HttpServletResponseCookie使
*/ */
public static void delLoginToken(HttpServletRequest request,HttpServletResponse response){ public static void delLoginToken(HttpServletRequest request, HttpServletResponse response) {
// 从请求中获取客户端携带的所有Cookie数组用于查找要删除的登录相关Cookie。
Cookie[] cks = request.getCookies(); Cookie[] cks = request.getCookies();
if(cks != null){ if (cks!= null) {
for(Cookie ck:cks) { for (Cookie ck : cks) {
if(StringUtils.equals(ck.getName(),COOKIE_NAME)){ // 通过比较Cookie的名称是否与预先定义的登录相关Cookie名称COOKIE_NAME相等来确定是否是要删除的Cookie。
if (StringUtils.equals(ck.getName(), COOKIE_NAME)) {
// 设置要删除的Cookie的域名确保与之前写入时的域名一致以便准确删除对应的Cookie。
ck.setDomain(COOKIE_DOMAIN); ck.setDomain(COOKIE_DOMAIN);
// 设置Cookie的路径为根目录"/"与之前写入时的路径设置保持一致保证删除的是对应路径下的该Cookie。
ck.setPath("/"); ck.setPath("/");
ck.setMaxAge(0);//0表示消除此cookie // 设置Cookie的最大存活时间为0表示立即删除该Cookie客户端收到响应后会将该Cookie从本地移除。
log.info("del cookieName:{},cookieBValue:{}",ck.getName(),ck.getValue()); ck.setMaxAge(0); //0表示消除此cookie
// 记录要删除的Cookie的名称和值的日志信息方便在调试等场景下查看具体的删除操作情况。
log.info("del cookieName:{},cookieBValue:{}", ck.getName(), ck.getValue());
// 将配置好的即将要删除的Cookie添加到响应中客户端收到响应后会根据设置删除对应的Cookie。
response.addCookie(ck); response.addCookie(ck);
return; return;
} }

@ -4,65 +4,117 @@ import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormatter;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
/** /**
* @DESC * @DESC
* Joda-TimeJava
* 便
*/ */
public class DateTimeUtil { public class DateTimeUtil {
//joda-time //joda-time
// 用于说明该工具类在处理时间相关操作时使用了Joda-Time库它是一个功能强大的日期时间处理库能更方便地进行各种日期时间的操作和格式化等工作。
//str->Date //str->Date
//Date->str //Date->str
public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss"; // 注释说明该工具类提供了将字符串转换为日期对象以及将日期对象转换为字符串这两种方向的时间转换功能,方便在不同业务场景下对时间数据进行处理。
public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
// 定义了一个标准的日期时间格式字符串常量,用于在没有指定特定格式时,作为默认的日期时间格式化模板,符合常见的年-月-日 时:分:秒的格式表示。
public static Date strToDate(String dateTimeStr, String formatStr){ /**
*
* 使Joda-TimeDateTimeFormatter
* DateTimeJavaDate
*
* @param dateTimeStr formatStr
* @param formatStr "yyyy-MM-dd HH:mm:ss"
* @return JavaDateJoda-Time
*/
public static Date strToDate(String dateTimeStr, String formatStr) {
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(formatStr); DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(formatStr);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr); DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate(); return dateTime.toDate();
} }
public static String dateToStr(Date date,String formatStr){ /**
if(date == null){ *
* nullnull
* null使Joda-TimeDateTime
*
* @param date JavaDate
* @param formatStr
* @return null
*/
public static String dateToStr(Date date, String formatStr) {
if (date == null) {
return StringUtils.EMPTY; return StringUtils.EMPTY;
} }
DateTime dateTime = new DateTime(date); DateTime dateTime = new DateTime(date);
return dateTime.toString(formatStr); return dateTime.toString(formatStr);
} }
//固定好格式 /**
public static Date strToDate(String dateTimeStr){ * "yyyy-MM-dd HH:mm:ss"
* 使STANDARD_FORMATDateTimeFormatter
* DateTimeJavaDate
*
* @param dateTimeStr "yyyy-MM-dd HH:mm:ss"
* @return JavaDateJoda-Time
*/
public static Date strToDate(String dateTimeStr) {
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(STANDARD_FORMAT); DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(STANDARD_FORMAT);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr); DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate(); return dateTime.toDate();
} }
public static String dateToStr(Date date){ /**
if(date == null){ * "yyyy-MM-dd HH:mm:ss"
* nullnull
* null使Joda-TimeDateTime
*
* @param date JavaDate
* @return null"yyyy-MM-dd HH:mm:ss"
*/
public static String dateToStr(Date date) {
if (date == null) {
return StringUtils.EMPTY; return StringUtils.EMPTY;
} }
DateTime dateTime = new DateTime(date); DateTime dateTime = new DateTime(date);
return dateTime.toString(STANDARD_FORMAT); return dateTime.toString(STANDARD_FORMAT);
} }
//Date -> 时间戳 /**
* 19701100:00:00 UTC
* nullnullnull
* null使JavaSimpleDateFormat"yyyy-MM-dd HH:mm:ss"
* Date
*
* @param date JavaDate
* @return Long19701100:00:00 UTCnullnull
* ParseException
*/
public static Long dateToChuo(Date date) throws ParseException { public static Long dateToChuo(Date date) throws ParseException {
if(date == null){ if (date == null) {
return null; return null;
} }
SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return format.parse(String.valueOf(date)).getTime(); return format.parse(String.valueOf(date)).getTime();
} }
/**
* Date
*
*
* @param args 使
* @throws ParseException DateJava
*/
public static void main(String[] args) throws ParseException { public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time="1970-01-06 11:45:55"; String time = "1970-01-06 11:45:55";
Date date = format.parse(time); Date date = format.parse(time);
System.out.print("Format To times:"+date.getTime()); System.out.print("Format To times:" + date.getTime());
} }
} }

@ -8,118 +8,153 @@ import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize; import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.type.JavaType; import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.type.TypeReference; import org.codehaus.jackson.type.TypeReference;
import java.io.IOException; import java.io.IOException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
/** /**
* jackson * jackson
* JacksonJSONJSON
* JacksonObjectMapperJSON
*/ */
@Slf4j @Slf4j
public class JsonUtil { public class JsonUtil {
// 创建一个ObjectMapper对象它是Jackson库中用于进行JSON序列化和反序列化操作的核心类后续的各种序列化和反序列化操作都依赖它来实现。
private static ObjectMapper objectMapper = new ObjectMapper(); private static ObjectMapper objectMapper = new ObjectMapper();
static { static {
//所有字段都列入进行转换 //所有字段都列入进行转换
// 设置ObjectMapper在序列化时的包含规则这里设置为JsonSerialize.Inclusion.ALWAYS表示所有的字段都会被包含进序列化的结果中
// 即使字段的值为null也会进行序列化处理确保完整的对象结构能在JSON字符串中体现出来。
objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.ALWAYS); objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.ALWAYS);
//取消默认转换timestamp形式 //取消默认转换timestamp形式
objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS,false); // 配置ObjectMapper在序列化日期类型字段时取消将日期转换为时间戳的默认行为这样在序列化后的JSON字符串中日期会以指定的格式呈现而不是时间戳形式。
objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
//忽略空bean转json的错误 //忽略空bean转json的错误
objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS,false); // 配置ObjectMapper在尝试将一个空的Java对象没有任何属性值的对象转换为JSON字符串时忽略可能出现的错误
// 使得即使对象为空也能正常进行序列化操作,避免抛出异常导致程序中断。
objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
//统一时间的格式 //统一时间的格式
// 设置ObjectMapper在处理日期类型数据时使用的统一日期格式通过传入一个SimpleDateFormat对象指定了格式为DateTimeUtil.STANDARD_FORMAT在其他地方定义的标准格式
// 保证序列化和反序列化过程中日期格式的一致性和规范性。
objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT)); objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT));
//忽略json存在属性但是java对象不存在属性的错误 //忽略json存在属性但是java对象不存在属性的错误
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES,false); // 配置ObjectMapper在进行反序列化时忽略JSON字符串中存在但对应的Java对象中不存在的属性所引发的错误
// 这样在JSON数据结构可能发生变化或者不完全匹配Java对象结构时依然能够正常进行反序列化操作提高了程序的兼容性和容错性。
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
} }
/** /**
* *
* @param obj * JavaJSONnullnull
* @param <T> * IOnull
* @return *
* @param obj Java<T>
* @param <T> JSON
* @return JSONnullnull
*/ */
public static <T> String obj2String(T obj){ public static <T> String obj2String(T obj) {
if(obj == null){ if (obj == null) {
return null; return null;
} }
try { try {
return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj); // 判断传入的对象是否本身就是字符串类型如果是则直接返回该字符串否则使用ObjectMapper将对象转换为JSON字符串并返回。
return obj instanceof String? (String) obj : objectMapper.writeValueAsString(obj);
} catch (IOException e) { } catch (IOException e) {
log.warn("parse object to string error",e); log.warn("parse object to string error", e);
return null; return null;
} }
} }
/** /**
* 便 * 便
* @param obj * obj2StringJavaJSON使JSON
* @param <T> * 使JSON便null
* @return *
* @param obj Java<T>
* @param <T> JSON
* @return JSONnullnullJSON
*/ */
public static <T> String obj2StringPretty(T obj){ public static <T> String obj2StringPretty(T obj) {
if(obj == null){ if (obj == null) {
return null; return null;
} }
try { try {
return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj); // 使用ObjectMapper的writerWithDefaultPrettyPrinter方法获取一个能够生成美化格式JSON字符串的写入器
// 然后通过该写入器将对象转换为美化后的JSON字符串并返回。
return obj instanceof String? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (IOException e) { } catch (IOException e) {
log.warn("parse object to string error",e); log.warn("parse object to string error", e);
return null; return null;
} }
} }
/** /**
* *
* @param str * JSONJavanull
* @param clazz * nullIOJSONnull
* @param <T> *
* @return * @param str JSONJava
* @param clazz JavaClass<T>JSON
* @param <T> clazz
* @return Javanullnull
*/ */
public static <T> T String2Obj(String str,Class<T> clazz){ public static <T> T String2Obj(String str, Class<T> clazz) {
if(StringUtils.isEmpty(str) || clazz == null){ if (StringUtils.isEmpty(str) || clazz == null) {
return null; return null;
} }
try { try {
return clazz.equals(String.class)?(T)str:objectMapper.readValue(str,clazz); // 判断传入的目标类是否就是字符串类型的Class对象如果是则直接将传入的字符串强制转换并返回因为本身就是字符串无需真正的反序列化操作
// 否则使用ObjectMapper将JSON字符串按照指定的目标类进行反序列化并返回对应的对象。
return clazz.equals(String.class)? (T) str : objectMapper.readValue(str, clazz);
} catch (IOException e) { } catch (IOException e) {
log.warn("parse string to obj error",e); log.warn("parse string to obj error", e);
return null; return null;
} }
} }
/** /**
* *
* @param str * JSON
* @param typeReference * TypeReferenceTypeReferencenullnull
* @param <T> * null
* @return *
* @param str JSONJava
* @param typeReference TypeReference
* @param <T> typeReference
* @return JavatypeReferencenullnull
*/ */
public static <T> T Str2Obj(String str, TypeReference typeReference){ public static <T> T Str2Obj(String str, TypeReference typeReference) {
if(StringUtils.isEmpty(str) || typeReference == null){ if (StringUtils.isEmpty(str) || typeReference == null) {
return null; return null;
} }
try { try {
return (T) (typeReference.getType().equals(String.class)?str:objectMapper.readValue(str,typeReference)); // 判断传入的TypeReference所表示的类型是否就是字符串类型如果是则直接返回传入的字符串无需反序列化操作
// 否则使用ObjectMapper按照TypeReference指定的复杂类型信息将JSON字符串进行反序列化并返回对应的对象。
return (T) (typeReference.getType().equals(String.class)? str : objectMapper.readValue(str, typeReference));
} catch (IOException e) { } catch (IOException e) {
log.warn("parse string to obj error",e); log.warn("parse string to obj error", e);
return null; return null;
} }
} }
/** /**
* *
* @param str * ClassClass
* @param collectionClass * ObjectMapperJavaType
* @param elementClasses * null
* @param <T> *
* @return * @param str JSONJava
* @param collectionClass ClassListSet
* @param elementClasses Class
* @param <T> collectionClasselementClasses
* @return Javanull
*/ */
public static <T> T Str2Obj(String str,Class<?> collectionClass,Class<?>... elementClasses){ public static <T> T Str2Obj(String str, Class<?> collectionClass, Class<?>... elementClasses) {
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass,elementClasses); JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
try { try {
return objectMapper.readValue(str,javaType); return objectMapper.readValue(str, javaType);
} catch (IOException e) { } catch (IOException e) {
log.warn("parse string to obj error",e); log.warn("parse string to obj error", e);
return null; return null;
} }
} }

Loading…
Cancel
Save