| @@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.*; | |||||
| import java.util.Map; | import java.util.Map; | ||||
| @RestController | @RestController | ||||
| @RequestMapping("wxMsgCallback") | |||||
| @RequestMapping("msgCallback") | |||||
| public class WxMsgCallbackController extends BaseController { | public class WxMsgCallbackController extends BaseController { | ||||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | private final Logger logger = LoggerFactory.getLogger(this.getClass()); | ||||
| @@ -79,33 +79,4 @@ public class WxMsgCallbackController extends BaseController { | |||||
| logger.debug("[" + getIpAddr() + "] WxMsgCallbackController::findById"); | logger.debug("[" + getIpAddr() + "] WxMsgCallbackController::findById"); | ||||
| return new ResultData(Result.SUCCESS, "查询成功", wxMsgCallbackService.getById(id)); | return new ResultData(Result.SUCCESS, "查询成功", wxMsgCallbackService.getById(id)); | ||||
| } | } | ||||
| @PostMapping(value = "/receivemsg/{tenantId}") | |||||
| public void receivemsg(@PathVariable String tenantId, @RequestParam Map<String, String> param) { | |||||
| logger.debug("[" + getIpAddr() + "] WxMsgCallbackController::receivemsg"); | |||||
| logger.info(param.toString()); | |||||
| //解析param数据插入数据库中 | |||||
| wxMsgCallbackService.saveOrUpdate(tenantId,param); | |||||
| } | |||||
| @RequestMapping(value = "/receivemodel/{tenantId}") | |||||
| public void receivemodel(@PathVariable String tenantId, @RequestParam Map<String, String> param) { | |||||
| logger.debug("[" + getIpAddr() + "] WxMsgCallbackController::receivemodel"); | |||||
| logger.info(param.toString()); | |||||
| //解析param数据插入数据库中 | |||||
| wxMsgCallbackService.receivemodel(tenantId, param); | |||||
| } | |||||
| @RequestMapping(value = "/receiveverifymodel/{tenantId}") | |||||
| public void receiveverifymodel(@PathVariable String tenantId, @RequestParam Map<String, String> param) { | |||||
| logger.debug("[" + getIpAddr() + "] WxMsgCallbackController::receiveverifymodel"); | |||||
| logger.info(param.toString()); | |||||
| //解析param数据插入数据库中 | |||||
| wxMsgCallbackService.receiveverifymodel(tenantId, param); | |||||
| } | |||||
| } | } | ||||
| @@ -1,81 +0,0 @@ | |||||
| package com.iformall.controller.official; | |||||
| import com.github.pagehelper.PageInfo; | |||||
| import com.iformall.common.Result; | |||||
| import com.iformall.common.ResultData; | |||||
| import com.iformall.controller.base.BaseController; | |||||
| import com.iformall.domain.po.WxMallApply; | |||||
| import com.iformall.domain.po.WxMsgValidationcode; | |||||
| import com.iformall.service.WxMallApplyService; | |||||
| import io.swagger.annotations.ApiImplicitParam; | |||||
| import io.swagger.annotations.ApiImplicitParams; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.web.bind.annotation.*; | |||||
| @RestController | |||||
| @RequestMapping("wxMallApply") | |||||
| public class WxMallApplyController extends BaseController { | |||||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||||
| @Autowired | |||||
| private WxMallApplyService wxMallApplyService; | |||||
| @GetMapping("list") | |||||
| @ApiImplicitParams({ | |||||
| @ApiImplicitParam(name = "pageNum", value = "页数", dataType = "int", paramType = "query", required = true), | |||||
| @ApiImplicitParam(name = "pageSize", value = "每页条数", dataType = "int", paramType = "query", required = true)}) | |||||
| public ResultData list(@ModelAttribute WxMallApply wxMallApply, Integer pageNum, Integer pageSize) { | |||||
| logger.debug("[" + getIpAddr() + "] WxMallApplyController::list"); | |||||
| if (null == wxMallApply) wxMallApply = new WxMallApply(); | |||||
| final PageInfo<WxMallApply> page = wxMallApplyService.listAsPage(wxMallApply, pageNum, pageSize); | |||||
| return new ResultData(page); | |||||
| } | |||||
| @PostMapping("add") | |||||
| public ResultData add(@RequestBody WxMallApply wxMallApply) { | |||||
| logger.debug("[" + getIpAddr() + "] WxMallApplyController::add"); | |||||
| //Assert.notNull(wxMallApply.getName(), "角色名不能为空"); | |||||
| //Assert.isTrue(!checkUnique(sysRole.getName(), null), "重复的角色名"); | |||||
| return wxMallApplyService.saveOrUpdate(wxMallApply); | |||||
| } | |||||
| @PostMapping("update") | |||||
| public ResultData update(@RequestBody WxMallApply wxMallApply) { | |||||
| logger.debug("[" + getIpAddr() + "] WxMallApplyController::update"); | |||||
| return wxMallApplyService.saveOrUpdate(wxMallApply); | |||||
| } | |||||
| @GetMapping("/del") | |||||
| @ApiImplicitParam(name = "id", value = "id", dataType = "String", paramType = "query", required = true) | |||||
| public ResultData delete(String id) { | |||||
| logger.debug("[" + getIpAddr() + "] WxMallApplyController::delete"); | |||||
| wxMallApplyService.deleteById(id); | |||||
| return new ResultData(Result.SUCCESS, "删除成功", null); | |||||
| } | |||||
| @GetMapping("/findById") | |||||
| @ApiImplicitParam(name = "id", value = "id", dataType = "String", paramType = "query", required = true) | |||||
| public ResultData findById(String id) { | |||||
| logger.debug("[" + getIpAddr() + "] WxMallApplyController::findById"); | |||||
| return new ResultData(Result.SUCCESS, "查询成功", wxMallApplyService.getById(id)); | |||||
| } | |||||
| @GetMapping("/sendvalidationcode") | |||||
| @ApiImplicitParams({ | |||||
| @ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true), | |||||
| @ApiImplicitParam(name = "type", value = "场景", dataType = "Integer", paramType = "query", required = true)}) | |||||
| public ResultData sendvalidationcode(String phone, Integer type) { | |||||
| logger.debug("[" + getIpAddr() + "] WxMallApplyController::sendvalidationcode"); | |||||
| WxMsgValidationcode wxMsgValidationcode = new WxMsgValidationcode(); | |||||
| wxMsgValidationcode.setTenantId("1"); | |||||
| wxMsgValidationcode.setPhone(phone); | |||||
| wxMsgValidationcode.setType(type); | |||||
| return wxMallApplyService.sendvalidationcode(wxMsgValidationcode); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,162 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
| <modelVersion>4.0.0</modelVersion> | |||||
| <parent> | |||||
| <artifactId>mallink</artifactId> | |||||
| <groupId>com.iformall</groupId> | |||||
| <version>1.0</version> | |||||
| </parent> | |||||
| <artifactId>mallinkCallback</artifactId> | |||||
| <properties> | |||||
| <weixin-java-mp.version>3.3.0</weixin-java-mp.version> | |||||
| <weixin-java-open.version>3.3.0</weixin-java-open.version> | |||||
| </properties> | |||||
| <dependencies> | |||||
| <dependency> | |||||
| <groupId>com.iformall</groupId> | |||||
| <artifactId>mallinkService</artifactId> | |||||
| <version>1.0</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.iformall</groupId> | |||||
| <artifactId>mybatis-multi-tenancy</artifactId> | |||||
| <version>1.0</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>commons-fileupload</groupId> | |||||
| <artifactId>commons-fileupload</artifactId> | |||||
| <version>1.3.3</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.google.zxing</groupId> | |||||
| <artifactId>core</artifactId> | |||||
| <version>3.3.3</version> | |||||
| </dependency> | |||||
| <dependency> | |||||
| <groupId>com.github.axet</groupId> | |||||
| <artifactId>kaptcha</artifactId> | |||||
| <version>0.0.9</version> | |||||
| </dependency> | |||||
| </dependencies> | |||||
| <build> | |||||
| <plugins> | |||||
| <plugin> | |||||
| <groupId>org.springframework.boot</groupId> | |||||
| <artifactId>spring-boot-maven-plugin</artifactId> | |||||
| <configuration> | |||||
| <executable>true</executable> | |||||
| <layout>ZIP</layout> | |||||
| <excludeGroupIds> | |||||
| antlr, | |||||
| cn.afterturn, | |||||
| ch.qos.logback, | |||||
| com.alibaba, | |||||
| com.amazonaws, | |||||
| com.mchange, | |||||
| com.fasterxml.jackson.core, | |||||
| com.fasterxml.jackson.dataformat, | |||||
| com.fasterxml.jackson.datatype, | |||||
| com.fasterxml.jackson.module, | |||||
| com.github.axet, | |||||
| com.github.binarywang, | |||||
| com.github.jsqlparser, | |||||
| com.github.pagehelper, | |||||
| com.github.ulisesbocchio, | |||||
| com.github.virtuald, | |||||
| com.google.code.gson, | |||||
| com.google.guava, | |||||
| com.google.protobuf, | |||||
| com.google.zxing, | |||||
| com.jayway.jsonpath, | |||||
| com.jhlabs, | |||||
| com.puppycrawl.tools, | |||||
| com.rabbitmq, | |||||
| com.squareup.okhttp3, | |||||
| com.squareup.okio, | |||||
| com.sun, | |||||
| com.sun.mail, | |||||
| com.thoughtworks.xstream, | |||||
| com.zaxxer, | |||||
| commons-beanutils, | |||||
| commons-cli, | |||||
| commons-codec, | |||||
| commons-collections, | |||||
| commons-io, | |||||
| commons-logging, | |||||
| io.lettuce, | |||||
| io.netty, | |||||
| io.projectreactor, | |||||
| io.springfox, | |||||
| io.swagger, | |||||
| io.undertow, | |||||
| javax.activation, | |||||
| javax.mail, | |||||
| javax.persistence, | |||||
| javax.xml.bind, | |||||
| joda-time, | |||||
| junit, | |||||
| mysql, | |||||
| net.bytebuddy, | |||||
| net.minidev, | |||||
| net.sf.saxon, | |||||
| ognl, | |||||
| org.antlr, | |||||
| org.apache.commons, | |||||
| org.apache.httpcomponents, | |||||
| org.apache.logging.log4j, | |||||
| org.apache.poi, | |||||
| org.apache.poi.wso2, | |||||
| org.apache.rocketmq, | |||||
| org.apache.shiro, | |||||
| org.apache.tomcat.embed, | |||||
| org.apache.xmlbeans, | |||||
| org.aspectj, | |||||
| org.assertj, | |||||
| org.bouncycastle, | |||||
| org.checkerframework, | |||||
| org.crazycake, | |||||
| org.dom4j, | |||||
| org.flowable, | |||||
| org.hibernate.validator, | |||||
| org.jasypt, | |||||
| org.javassist, | |||||
| org.jboss.spec.javax.annotation, | |||||
| org.jboss.xnio, | |||||
| org.jdom, | |||||
| org.mockito, | |||||
| org.mybatis, | |||||
| org.mybatis.generator, | |||||
| org.mybatis.spring.boot, | |||||
| org.ow2.asm, | |||||
| org.projectlombok, | |||||
| org.quartz-scheduler, | |||||
| org.rocketmq.spring.boot, | |||||
| org.springframework, | |||||
| org.springframework.amqp, | |||||
| org.springframework.boot, | |||||
| org.springframework.data, | |||||
| org.springframework.retry, | |||||
| org.springframework.ws, | |||||
| org.yaml, | |||||
| redis.clients, | |||||
| software.amazon.ion, | |||||
| tk.mybatis, | |||||
| xmlpull, | |||||
| xpp3 | |||||
| </excludeGroupIds> | |||||
| </configuration> | |||||
| </plugin> | |||||
| </plugins> | |||||
| </build> | |||||
| </project> | |||||
| @@ -0,0 +1,52 @@ | |||||
| package com.iformall; | |||||
| import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties; | |||||
| import org.rocketmq.starter.annotation.EnableRocketMQ; | |||||
| import org.springframework.beans.factory.annotation.Value; | |||||
| import org.springframework.boot.SpringApplication; | |||||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | |||||
| import org.springframework.context.annotation.Bean; | |||||
| import org.springframework.scheduling.annotation.EnableAsync; | |||||
| import springfox.documentation.swagger2.annotations.EnableSwagger2; | |||||
| import tk.mybatis.spring.annotation.MapperScan; | |||||
| /** | |||||
| * @author chenkx | |||||
| * @date 2017-12-26 | |||||
| */ | |||||
| @SpringBootApplication | |||||
| @MapperScan(basePackages = {"com.iformall.mapper"}) | |||||
| @EnableSwagger2 | |||||
| @EnableEncryptableProperties | |||||
| @EnableAsync | |||||
| @EnableRocketMQ | |||||
| public class CallbackApplication { | |||||
| @Value("${fm.exception}") | |||||
| private boolean fmException; | |||||
| @Value("${fm.exception_emails}") | |||||
| private String fmExceptionEmails; | |||||
| @Value("${fm.open}") | |||||
| private boolean fmOpen; | |||||
| @Bean | |||||
| public boolean isFmException() { | |||||
| return fmException; | |||||
| } | |||||
| @Bean | |||||
| public String fmExceptionEmails() { | |||||
| return fmExceptionEmails; | |||||
| } | |||||
| @Bean | |||||
| public boolean isFmOpen() { | |||||
| return fmOpen; | |||||
| } | |||||
| public static void main(String[] args) { | |||||
| SpringApplication.run(CallbackApplication.class, args); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,10 @@ | |||||
| package com.iformall.annotation; | |||||
| import java.lang.annotation.*; | |||||
| @Target({ElementType.PARAMETER, ElementType.METHOD}) | |||||
| @Retention(RetentionPolicy.RUNTIME) | |||||
| @Documented | |||||
| public @interface SystemControllerLog { | |||||
| String description() default ""; | |||||
| } | |||||
| @@ -0,0 +1,10 @@ | |||||
| package com.iformall.annotation; | |||||
| import java.lang.annotation.*; | |||||
| @Target({ElementType.PARAMETER, ElementType.METHOD}) | |||||
| @Retention(RetentionPolicy.RUNTIME) | |||||
| @Documented | |||||
| public @interface SystemServiceLog { | |||||
| String description() default ""; | |||||
| } | |||||
| @@ -0,0 +1,52 @@ | |||||
| package com.iformall.config; | |||||
| import org.springframework.boot.context.properties.ConfigurationProperties; | |||||
| import org.springframework.stereotype.Component; | |||||
| /** | |||||
| * @author Stormeye | |||||
| */ | |||||
| @Component | |||||
| @ConfigurationProperties(prefix = "aws") | |||||
| public class AwsProperty { | |||||
| // AWS ACCESS KEY | |||||
| private String access; | |||||
| private String secret; | |||||
| private String clientRegion; | |||||
| private String bucketName; | |||||
| public String getAccess() { | |||||
| return access; | |||||
| } | |||||
| public void setAccess(String access) { | |||||
| this.access = access; | |||||
| } | |||||
| public String getSecret() { | |||||
| return secret; | |||||
| } | |||||
| public void setSecret(String secret) { | |||||
| this.secret = secret; | |||||
| } | |||||
| public String getClientRegion() { | |||||
| return clientRegion; | |||||
| } | |||||
| public void setClientRegion(String clientRegion) { | |||||
| this.clientRegion = clientRegion; | |||||
| } | |||||
| public String getBucketName() { | |||||
| return bucketName; | |||||
| } | |||||
| public void setBucketName(String bucketName) { | |||||
| this.bucketName = bucketName; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,26 @@ | |||||
| package com.iformall.config; | |||||
| import org.springframework.context.annotation.Bean; | |||||
| import org.springframework.context.annotation.Configuration; | |||||
| import org.springframework.core.task.AsyncTaskExecutor; | |||||
| import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | |||||
| import java.util.concurrent.ThreadPoolExecutor; | |||||
| @Configuration | |||||
| public class MyExecutorConfig { | |||||
| /** | |||||
| * 自定义异步线程池 | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| @Bean | |||||
| public AsyncTaskExecutor taskExecutor() { | |||||
| ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); | |||||
| executor.setThreadNamePrefix("Anno-Executor"); | |||||
| executor.setMaxPoolSize(100); | |||||
| executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); | |||||
| return executor; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,145 @@ | |||||
| package com.iformall.config; | |||||
| import com.iformall.domain.po.PushLimit; | |||||
| import com.iformall.domain.po.WxCUser; | |||||
| import com.iformall.domain.po.WxScoreRules; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.cache.CacheManager; | |||||
| import org.springframework.cache.annotation.CachingConfigurerSupport; | |||||
| import org.springframework.cache.annotation.EnableCaching; | |||||
| import org.springframework.context.annotation.Bean; | |||||
| import org.springframework.context.annotation.Configuration; | |||||
| import org.springframework.data.redis.cache.RedisCacheConfiguration; | |||||
| import org.springframework.data.redis.cache.RedisCacheManager; | |||||
| import org.springframework.data.redis.cache.RedisCacheWriter; | |||||
| import org.springframework.data.redis.connection.RedisConnectionFactory; | |||||
| import org.springframework.data.redis.core.RedisTemplate; | |||||
| import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; | |||||
| import org.springframework.data.redis.serializer.StringRedisSerializer; | |||||
| import java.time.Duration; | |||||
| import java.util.HashMap; | |||||
| import java.util.HashSet; | |||||
| import java.util.Map; | |||||
| import java.util.Set; | |||||
| /** | |||||
| * Created by Stormeye on 2018/10/1. | |||||
| */ | |||||
| @Configuration | |||||
| @EnableCaching | |||||
| public class RedisConfig extends CachingConfigurerSupport { | |||||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||||
| //缓存管理器 | |||||
| @Bean | |||||
| public CacheManager cacheManager(RedisConnectionFactory connectionFactory) { | |||||
| /* | |||||
| //user信息缓存配置 | |||||
| RedisCacheConfiguration userCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(10)).disableCachingNullValues().prefixKeysWith("user"); | |||||
| Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>(); | |||||
| redisCacheConfigurationMap.put("user", userCacheConfiguration); | |||||
| //初始化一个RedisCacheWriter | |||||
| RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory); | |||||
| // 设置CacheManager的值序列化方式为JdkSerializationRedisSerializer,但其实RedisCacheConfiguration默认就是使用StringRedisSerializer序列化key,JdkSerializationRedisSerializer序列化value,所以以下注释代码为默认实现 | |||||
| // ClassLoader loader = this.getClass().getClassLoader(); | |||||
| // JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(loader); | |||||
| // RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializer); | |||||
| // RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair); | |||||
| RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig(); | |||||
| //设置默认超过期时间是30秒 | |||||
| defaultCacheConfig.entryTtl(Duration.ofSeconds(30)); | |||||
| //初始化RedisCacheManager | |||||
| RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig, redisCacheConfigurationMap); | |||||
| return cacheManager; | |||||
| */ | |||||
| RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); // 生成一个默认配置,通过config对象即可对缓存进行自定义配置 | |||||
| config = config.entryTtl(Duration.ofMinutes(1)) // 设置缓存的默认过期时间,也是使用Duration设置 | |||||
| .disableCachingNullValues(); // 不缓存空值 | |||||
| // 设置一个初始化的缓存空间set集合 | |||||
| Set<String> cacheNames = new HashSet<>(); | |||||
| cacheNames.add("my-redis-cache1"); | |||||
| cacheNames.add("my-redis-cache2"); | |||||
| // 对每个缓存空间应用不同的配置 | |||||
| Map<String, RedisCacheConfiguration> configMap = new HashMap<>(); | |||||
| configMap.put("my-redis-cache1", config); | |||||
| configMap.put("my-redis-cache2", config.entryTtl(Duration.ofSeconds(120))); | |||||
| RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory) // 使用自定义的缓存配置初始化一个cacheManager | |||||
| .initialCacheNames(cacheNames) // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置 | |||||
| .withInitialCacheConfigurations(configMap) | |||||
| .build(); | |||||
| return cacheManager; | |||||
| } | |||||
| @Bean("pushLimitRedisTemplate") | |||||
| public RedisTemplate<String, PushLimit> getPushLimitRedisTemplate(RedisConnectionFactory connectionFactory) { | |||||
| RedisTemplate<String, PushLimit> template = new RedisTemplate<String, PushLimit>(); | |||||
| Jackson2JsonRedisSerializer<PushLimit> j = new Jackson2JsonRedisSerializer<PushLimit>(PushLimit.class); | |||||
| // value值的序列化 | |||||
| template.setValueSerializer(j); | |||||
| template.setHashKeySerializer(j); | |||||
| // key的序列化 | |||||
| template.setKeySerializer(new StringRedisSerializer()); | |||||
| template.setHashKeySerializer(new StringRedisSerializer()); | |||||
| template.setConnectionFactory(connectionFactory); | |||||
| return template; | |||||
| } | |||||
| @Bean("scoreRuleRedisTemplate") | |||||
| public RedisTemplate<String, WxScoreRules> getScoreRuleRedisTemplate(RedisConnectionFactory connectionFactory) { | |||||
| RedisTemplate<String, WxScoreRules> template = new RedisTemplate<String, WxScoreRules>(); | |||||
| Jackson2JsonRedisSerializer<WxScoreRules> j = new Jackson2JsonRedisSerializer<WxScoreRules>(WxScoreRules.class); | |||||
| // value值的序列化 | |||||
| template.setValueSerializer(j); | |||||
| template.setHashKeySerializer(j); | |||||
| // key的序列化 | |||||
| template.setKeySerializer(new StringRedisSerializer()); | |||||
| template.setHashKeySerializer(new StringRedisSerializer()); | |||||
| template.setConnectionFactory(connectionFactory); | |||||
| return template; | |||||
| } | |||||
| @Bean("openRedisTemplate") | |||||
| public RedisTemplate<String, String> getWeChatOpen(RedisConnectionFactory connectionFactory) { | |||||
| RedisTemplate<String, String> template = new RedisTemplate<String, String>(); | |||||
| // value值的序列化 | |||||
| template.setValueSerializer(new StringRedisSerializer()); | |||||
| // key的序列化 | |||||
| template.setKeySerializer(new StringRedisSerializer()); | |||||
| template.setHashKeySerializer(new StringRedisSerializer()); | |||||
| template.setConnectionFactory(connectionFactory); | |||||
| return template; | |||||
| } | |||||
| @Bean("cuserTokenRedisTemplate") | |||||
| public RedisTemplate<String, WxCUser> getCUserTokenRedisTemplate(RedisConnectionFactory connectionFactory) { | |||||
| RedisTemplate<String, WxCUser> template = new RedisTemplate<String, WxCUser>(); | |||||
| Jackson2JsonRedisSerializer<WxCUser> j = new Jackson2JsonRedisSerializer<WxCUser>(WxCUser.class); | |||||
| // value值的序列化 | |||||
| template.setValueSerializer(j); | |||||
| template.setHashKeySerializer(j); | |||||
| // key的序列化 | |||||
| template.setKeySerializer(new StringRedisSerializer()); | |||||
| template.setHashKeySerializer(new StringRedisSerializer()); | |||||
| template.setConnectionFactory(connectionFactory); | |||||
| return template; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,58 @@ | |||||
| package com.iformall.config; | |||||
| import java.io.IOException; | |||||
| import java.util.Optional; | |||||
| import javax.servlet.Filter; | |||||
| import javax.servlet.FilterChain; | |||||
| import javax.servlet.FilterConfig; | |||||
| import javax.servlet.ServletException; | |||||
| import javax.servlet.ServletRequest; | |||||
| import javax.servlet.ServletResponse; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import javax.servlet.http.HttpServletResponse; | |||||
| /** | |||||
| * 前后端分离RESTful接口过滤器 | |||||
| * | |||||
| * @author xuguoqin | |||||
| * | |||||
| */ | |||||
| public class RestFilter implements Filter { | |||||
| @Override | |||||
| public void init(FilterConfig filterConfig) throws ServletException { | |||||
| } | |||||
| @Override | |||||
| public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | |||||
| throws IOException, ServletException { | |||||
| HttpServletRequest req = null; | |||||
| if (request instanceof HttpServletRequest) { | |||||
| req = (HttpServletRequest) request; | |||||
| } | |||||
| HttpServletResponse res = null; | |||||
| if (response instanceof HttpServletResponse) { | |||||
| res = (HttpServletResponse) response; | |||||
| } | |||||
| if (req != null && res != null) { | |||||
| //设置允许传递的参数 | |||||
| res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization"); | |||||
| //设置允许带上cookie | |||||
| res.setHeader("Access-Control-Allow-Credentials", "true"); | |||||
| String origin = Optional.ofNullable(req.getHeader("Origin")).orElse(req.getHeader("Referer")); | |||||
| //设置允许的请求来源 | |||||
| res.setHeader("Access-Control-Allow-Origin", origin); | |||||
| //设置允许的请求方法 | |||||
| res.setHeader("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS"); | |||||
| } | |||||
| chain.doFilter(request, response); | |||||
| } | |||||
| @Override | |||||
| public void destroy() { | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,24 @@ | |||||
| package com.iformall.config; | |||||
| import org.springframework.context.annotation.Bean; | |||||
| import org.springframework.context.annotation.Configuration; | |||||
| import org.springframework.http.client.ClientHttpRequestFactory; | |||||
| import org.springframework.http.client.SimpleClientHttpRequestFactory; | |||||
| import org.springframework.web.client.RestTemplate; | |||||
| @Configuration | |||||
| public class RestTemplateConfig { | |||||
| @Bean | |||||
| public RestTemplate restTemplate(ClientHttpRequestFactory factory) { | |||||
| return new RestTemplate(factory); | |||||
| } | |||||
| @Bean | |||||
| public ClientHttpRequestFactory simpleClientHttpRequestFactory() { | |||||
| SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); | |||||
| factory.setReadTimeout(5000);//ms | |||||
| factory.setConnectTimeout(10000);//ms | |||||
| return factory; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,307 @@ | |||||
| package com.iformall.config; | |||||
| import com.iformall.service.MallPermissionService; | |||||
| import com.iformall.shiro.MyRetryLimitCredentialsMatcher; | |||||
| import com.iformall.shiro.MyShiroRealm; | |||||
| import org.apache.shiro.mgt.SecurityManager; | |||||
| import org.apache.shiro.spring.LifecycleBeanPostProcessor; | |||||
| import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; | |||||
| import org.apache.shiro.spring.web.ShiroFilterFactoryBean; | |||||
| import org.apache.shiro.web.mgt.DefaultWebSecurityManager; | |||||
| import org.apache.shiro.web.servlet.SimpleCookie; | |||||
| import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; | |||||
| import org.crazycake.shiro.RedisCacheManager; | |||||
| import org.crazycake.shiro.RedisManager; | |||||
| import org.crazycake.shiro.RedisSessionDAO; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.beans.factory.annotation.Qualifier; | |||||
| import org.springframework.beans.factory.annotation.Value; | |||||
| import org.springframework.context.annotation.Bean; | |||||
| import org.springframework.context.annotation.Configuration; | |||||
| import javax.servlet.Filter; | |||||
| import javax.servlet.ServletRequest; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import java.util.LinkedHashMap; | |||||
| import java.util.Map; | |||||
| /** | |||||
| * Created by yangqj on 2017/4/23. | |||||
| */ | |||||
| @Configuration | |||||
| public class ShiroConfig { | |||||
| @Autowired(required = false) | |||||
| private MallPermissionService resourcesService; | |||||
| @Value("${spring.redis.host}") | |||||
| private String host; | |||||
| @Value("${spring.redis.port}") | |||||
| private int port; | |||||
| @Value("${spring.redis.timeout}") | |||||
| private int timeout; | |||||
| @Value("${spring.redis.expire}") | |||||
| private int expire; | |||||
| @Value("${spring.redis.password}") | |||||
| private String password; | |||||
| @Bean | |||||
| public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { | |||||
| return new LifecycleBeanPostProcessor(); | |||||
| } | |||||
| /** | |||||
| * ShiroDialect,为了在thymeleaf里使用shiro的标签的bean | |||||
| * @return | |||||
| */ | |||||
| // @Bean | |||||
| // public ShiroDialect shiroDialect() { | |||||
| // return new ShiroDialect(); | |||||
| // } | |||||
| /** | |||||
| * ShiroFilterFactoryBean 处理拦截资源文件问题。 | |||||
| * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在 | |||||
| * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager | |||||
| * <p> | |||||
| * Filter Chain定义说明 | |||||
| * 1、一个URL可以配置多个Filter,使用逗号分隔 | |||||
| * 2、当设置多个过滤器时,全部验证通过,才视为通过 | |||||
| * 3、部分过滤器可指定参数,如perms,roles | |||||
| */ | |||||
| @Bean | |||||
| public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { | |||||
| System.out.println("ShiroConfiguration.shirFilter()"); | |||||
| ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); | |||||
| // 必须设置 SecurityManager | |||||
| shiroFilterFactoryBean.setSecurityManager(securityManager); | |||||
| Map<String, Filter> filters = new LinkedHashMap<String, Filter>(); | |||||
| filters.put("token", new ShiroLoginFilter()); | |||||
| filters.put("corsFilter", new RestFilter()); | |||||
| //filters.put("authc", new MyFormAuthenticationFilter()); | |||||
| shiroFilterFactoryBean.setFilters(filters); | |||||
| // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 | |||||
| shiroFilterFactoryBean.setLoginUrl("/#/"); | |||||
| // 登录成功后要跳转的链接 | |||||
| shiroFilterFactoryBean.setSuccessUrl("/usersPage"); | |||||
| //未授权界面; | |||||
| shiroFilterFactoryBean.setUnauthorizedUrl("/403"); | |||||
| //拦截器. | |||||
| Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); | |||||
| // 官网 | |||||
| filterChainDefinitionMap.put("/wxMallApply/add", "anon"); | |||||
| filterChainDefinitionMap.put("/wxMallApply/sendvalidationcode", "anon"); | |||||
| // 硬件访问 | |||||
| filterChainDefinitionMap.put("/wxDeviceScreenAd/**", "anon"); | |||||
| // callback | |||||
| filterChainDefinitionMap.put("/wxPay/notify/**", "anon"); // 支付回调 | |||||
| filterChainDefinitionMap.put("/wxPayBill/notify/**", "anon"); | |||||
| filterChainDefinitionMap.put("/wxMsgCallback/**", "anon"); | |||||
| filterChainDefinitionMap.put("/carCallback/**", "anon"); | |||||
| // static files | |||||
| filterChainDefinitionMap.put("/css/**", "anon"); | |||||
| filterChainDefinitionMap.put("/js/**", "anon"); | |||||
| filterChainDefinitionMap.put("/img/**", "anon"); | |||||
| filterChainDefinitionMap.put("/font-awesome/**", "anon"); | |||||
| //<!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了; | |||||
| //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--> | |||||
| //自定义加载权限资源关系 | |||||
| // Map<String,Object> map = new HashMap<>(); | |||||
| // List<SysPermission> resourcesList = resourcesService.list(map); | |||||
| // for(SysPermission resources:resourcesList){ | |||||
| // | |||||
| // if (StringUtil.isNotEmpty(resources.getUrl())) { | |||||
| // String permission = "perms[" + resources.getUrl()+ "]"; | |||||
| // filterChainDefinitionMap.put(resources.getUrl(),permission); | |||||
| // } | |||||
| // } | |||||
| // swagger-ui | |||||
| filterChainDefinitionMap.put("/swagger-ui.html", "anon"); | |||||
| filterChainDefinitionMap.put("/v2/**", "anon"); | |||||
| filterChainDefinitionMap.put("/swagger-resources/**", "anon"); | |||||
| filterChainDefinitionMap.put("/webjars/**", "anon"); | |||||
| filterChainDefinitionMap.put("/version", "anon"); | |||||
| // filterChainDefinitionMap.put("/role/**", "corsFilter,token"); | |||||
| //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了 | |||||
| filterChainDefinitionMap.put("/logout", "authc"); | |||||
| filterChainDefinitionMap.put("/**", "corsFilter,token,authc"); | |||||
| // filterChainDefinitionMap.put("/**", "anon"); | |||||
| shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); | |||||
| return shiroFilterFactoryBean; | |||||
| } | |||||
| public static boolean isAjax(ServletRequest request) { | |||||
| String header = ((HttpServletRequest) request).getHeader("X-Requested-With"); | |||||
| if ("XMLHttpRequest".equalsIgnoreCase(header)) { | |||||
| System.out.println("当前请求为Ajax请求"); | |||||
| return Boolean.TRUE; | |||||
| } | |||||
| System.out.println("当前请求非Ajax请求"); | |||||
| return Boolean.FALSE; | |||||
| } | |||||
| @Bean | |||||
| public SecurityManager securityManager(@Qualifier("myRetryLimitCredentialsMatcher") MyRetryLimitCredentialsMatcher matcher) { | |||||
| DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); | |||||
| //设置realm. | |||||
| securityManager.setRealm(myShiroRealm(matcher)); | |||||
| // 自定义缓存实现 使用redis | |||||
| //securityManager.setCacheManager(cacheManager()); | |||||
| // 自定义session管理 使用redis | |||||
| securityManager.setSessionManager(sessionManager()); | |||||
| return securityManager; | |||||
| } | |||||
| @Bean | |||||
| public MyShiroRealm myShiroRealm(MyRetryLimitCredentialsMatcher matcher) { | |||||
| MyShiroRealm myShiroRealm = new MyShiroRealm(); | |||||
| myShiroRealm.setCredentialsMatcher(matcher); | |||||
| return myShiroRealm; | |||||
| } | |||||
| /** | |||||
| * 密码匹配凭证管理器 凭证匹配器 | |||||
| * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 | |||||
| * 所以我们需要修改下doGetAuthenticationInfo中的代码; | |||||
| * ) | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| @Bean(name = "myRetryLimitCredentialsMatcher") | |||||
| public MyRetryLimitCredentialsMatcher hashedCredentialsMatcher() { | |||||
| MyRetryLimitCredentialsMatcher hashedCredentialsMatcher = new MyRetryLimitCredentialsMatcher(); | |||||
| hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法; | |||||
| hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5("")); | |||||
| return hashedCredentialsMatcher; | |||||
| } | |||||
| /** | |||||
| * 开启shiro aop注解支持. | |||||
| * 使用代理方式;所以需要开启代码支持; | |||||
| * | |||||
| * @param securityManager | |||||
| * @return | |||||
| */ | |||||
| @Bean | |||||
| public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { | |||||
| AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); | |||||
| authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); | |||||
| return authorizationAttributeSourceAdvisor; | |||||
| } | |||||
| /** | |||||
| * 配置shiro redisManager | |||||
| * 使用的是shiro-redis开源插件 | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| public RedisManager redisManager() { | |||||
| RedisManager redisManager = new RedisManager(); | |||||
| redisManager.setHost(host); | |||||
| redisManager.setPort(port); | |||||
| //redisManager.setExpire(expire);// 配置缓存过期时间 | |||||
| redisManager.setTimeout(timeout); | |||||
| redisManager.setPassword(password); | |||||
| return redisManager; | |||||
| } | |||||
| /** | |||||
| * cacheManager 缓存 redis实现 | |||||
| * 使用的是shiro-redis开源插件 | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| public RedisCacheManager cacheManager() { | |||||
| RedisCacheManager redisCacheManager = new RedisCacheManager(); | |||||
| redisCacheManager.setRedisManager(redisManager()); | |||||
| return redisCacheManager; | |||||
| } | |||||
| /** | |||||
| * RedisSessionDAO shiro sessionDao层的实现 通过redis | |||||
| * 使用的是shiro-redis开源插件 | |||||
| */ | |||||
| @Bean | |||||
| public RedisSessionDAO redisSessionDAO() { | |||||
| RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); | |||||
| redisSessionDAO.setRedisManager(redisManager()); | |||||
| return redisSessionDAO; | |||||
| } | |||||
| /** | |||||
| * shiro session的管理 | |||||
| */ | |||||
| @Bean | |||||
| public DefaultWebSessionManager sessionManager() { | |||||
| DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); | |||||
| //设置session过期时间为1小时(单位:毫秒),默认为30分钟 | |||||
| sessionManager.setGlobalSessionTimeout(60 * 60 * 1000); | |||||
| sessionManager.setSessionValidationSchedulerEnabled(true); | |||||
| sessionManager.setSessionIdUrlRewritingEnabled(false); | |||||
| sessionManager.setSessionDAO(redisSessionDAO()); | |||||
| sessionManager.setSessionIdCookie(simpleCookie()); | |||||
| return sessionManager; | |||||
| } | |||||
| @Bean | |||||
| public SimpleCookie simpleCookie() { | |||||
| SimpleCookie simpleCookie = new SimpleCookie("SSIDS"); | |||||
| simpleCookie.setDomain(""); | |||||
| return simpleCookie; | |||||
| } | |||||
| // @Bean | |||||
| // public SimpleCookie rememberMeCookie(){ | |||||
| // //System.out.println("ShiroConfiguration.rememberMeCookie()"); | |||||
| // //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe | |||||
| // SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); | |||||
| // //<!-- 记住我cookie生效时间30天 ,单位秒;--> | |||||
| // simpleCookie.setMaxAge(60*30); | |||||
| // return simpleCookie; | |||||
| // } | |||||
| // @Bean | |||||
| // public CookieRememberMeManager rememberMeManager(){ | |||||
| // //System.out.println("ShiroConfiguration.rememberMeManager()"); | |||||
| // CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); | |||||
| // cookieRememberMeManager.setCookie(rememberMeCookie()); | |||||
| // //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位) | |||||
| // cookieRememberMeManager.setCipherKey(Base64.decodeBytes("2AvVhdsgUs0FSA3SDFAdag==")); | |||||
| // return cookieRememberMeManager; | |||||
| // } | |||||
| // @Bean(name = "securityManager") | |||||
| // public DefaultWebSecurityManager defaultWebSecurityManager(MyShiroRealm realm){ | |||||
| // DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); | |||||
| // //设置realm | |||||
| // securityManager.setRealm(realm); | |||||
| // //用户授权/认证信息Cache, 采用EhCache缓存 | |||||
| // securityManager.setCacheManager(cacheManager()); | |||||
| // //注入记住我管理器 | |||||
| // securityManager.setRememberMeManager(rememberMeManager()); | |||||
| // return securityManager; | |||||
| // } | |||||
| } | |||||
| @@ -0,0 +1,31 @@ | |||||
| package com.iformall.config; | |||||
| import javax.servlet.ServletRequest; | |||||
| import javax.servlet.ServletResponse; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; | |||||
| import com.alibaba.fastjson.JSON; | |||||
| import com.iformall.common.ResultData; | |||||
| public class ShiroLoginFilter extends FormAuthenticationFilter { | |||||
| @Override | |||||
| protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { | |||||
| response.setCharacterEncoding("UTF-8"); | |||||
| response.setContentType("application/json"); | |||||
| ResultData resultData = new ResultData(ResultData.UNLOGIN,"用户未登录"); | |||||
| response.getWriter().write(JSON.toJSONString(resultData)); | |||||
| return false; | |||||
| } | |||||
| /** | |||||
| * 判断ajax请求 | |||||
| * @param request | |||||
| * @return | |||||
| */ | |||||
| boolean isAjax(HttpServletRequest request){ | |||||
| return (request.getHeader("X-Requested-With") != null && "XMLHttpRequest".equals( request.getHeader("X-Requested-With").toString()) ) ; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,131 @@ | |||||
| package com.iformall.config; | |||||
| import com.fasterxml.jackson.annotation.JsonInclude; | |||||
| import com.fasterxml.jackson.databind.DeserializationConfig; | |||||
| import com.fasterxml.jackson.databind.DeserializationFeature; | |||||
| import com.fasterxml.jackson.databind.ObjectMapper; | |||||
| import com.fasterxml.jackson.databind.module.SimpleModule; | |||||
| import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; | |||||
| import com.iformall.interceptor.HttpServletRequestWrapperFilter; | |||||
| import com.iformall.interceptor.RequestInterceptor; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.boot.context.properties.EnableConfigurationProperties; | |||||
| import org.springframework.boot.web.servlet.FilterRegistrationBean; | |||||
| import org.springframework.context.annotation.Bean; | |||||
| import org.springframework.context.annotation.Configuration; | |||||
| import org.springframework.http.converter.HttpMessageConverter; | |||||
| import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; | |||||
| import org.springframework.web.cors.CorsConfiguration; | |||||
| import org.springframework.web.cors.UrlBasedCorsConfigurationSource; | |||||
| import org.springframework.web.filter.CharacterEncodingFilter; | |||||
| import org.springframework.web.filter.CorsFilter; | |||||
| import org.springframework.web.servlet.config.annotation.*; | |||||
| import javax.servlet.Filter; | |||||
| import java.math.BigDecimal; | |||||
| import java.math.BigInteger; | |||||
| import java.text.SimpleDateFormat; | |||||
| import java.util.List; | |||||
| @Configuration | |||||
| @EnableWebMvc | |||||
| @EnableConfigurationProperties({AwsProperty.class}) | |||||
| public class WebConfig implements WebMvcConfigurer { | |||||
| @Autowired | |||||
| private RequestInterceptor requestInterceptor; | |||||
| @Bean | |||||
| public CorsFilter corsFilter() { | |||||
| final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); | |||||
| final CorsConfiguration corsConfiguration = new CorsConfiguration(); | |||||
| // 允许cookies跨域 | |||||
| corsConfiguration.setAllowCredentials(true); | |||||
| // 允许向该服务器提交请求的URI, *表示全部允许 | |||||
| corsConfiguration.addAllowedOrigin("*"); | |||||
| // 允许访问的头信息,*表示全部 | |||||
| corsConfiguration.addAllowedHeader("*"); | |||||
| // 允许提交请求的方法, *表示全部允许 | |||||
| corsConfiguration.addAllowedMethod("*"); | |||||
| urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); | |||||
| return new CorsFilter(urlBasedCorsConfigurationSource); | |||||
| } | |||||
| /** | |||||
| * 用于处理编码问题 | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| @Bean | |||||
| public Filter characterEncodingFilter() { | |||||
| CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); | |||||
| characterEncodingFilter.setEncoding("UTF-8"); | |||||
| characterEncodingFilter.setForceEncoding(true); | |||||
| return characterEncodingFilter; | |||||
| } | |||||
| @Override | |||||
| public void addInterceptors(InterceptorRegistry registry) { | |||||
| registry.addInterceptor(requestInterceptor).addPathPatterns("/**"); | |||||
| } | |||||
| @Override | |||||
| public void addResourceHandlers(ResourceHandlerRegistry registry) { | |||||
| registry.addResourceHandler("swagger-ui.html") | |||||
| .addResourceLocations("classpath:/META-INF/resources/"); | |||||
| registry.addResourceHandler("/webjars/**") | |||||
| .addResourceLocations("classpath:/META-INF/resources/webjars/"); | |||||
| } | |||||
| @Override | |||||
| public void addCorsMappings(CorsRegistry registry) { | |||||
| registry.addMapping("/**") | |||||
| .allowedOrigins("*") | |||||
| .allowCredentials(true) | |||||
| .allowedMethods("GET", "POST", "DELETE", "PUT") | |||||
| .maxAge(3600); | |||||
| } | |||||
| @Override | |||||
| public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { | |||||
| MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); | |||||
| //ObjectMapper 是Jackson库的主要类。它提供一些功能将转换成Java对象匹配JSON结构,反之亦然 | |||||
| ObjectMapper objectMapper = new ObjectMapper(); | |||||
| SimpleModule simpleModule = new SimpleModule(); | |||||
| //不显示为null的字段 | |||||
| objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); | |||||
| DeserializationConfig dc = objectMapper.getDeserializationConfig(); | |||||
| // 设置反序列化日期格式、忽略不存在get、set的属性 | |||||
| objectMapper.setConfig( | |||||
| dc.with(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")) | |||||
| .without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) | |||||
| ); | |||||
| //序列化将Long转String类型 | |||||
| simpleModule.addSerializer(Long.class, ToStringSerializer.instance); | |||||
| simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); | |||||
| SimpleModule bigIntegerModule = new SimpleModule(); | |||||
| //序列化将BigInteger转String类型 | |||||
| bigIntegerModule.addSerializer(BigInteger.class, ToStringSerializer.instance); | |||||
| SimpleModule bigDecimalModule = new SimpleModule(); | |||||
| //序列化将BigDecimal转String类型 | |||||
| bigDecimalModule.addSerializer(BigDecimal.class, ToStringSerializer.instance); | |||||
| objectMapper.registerModule(simpleModule); | |||||
| objectMapper.registerModule(bigDecimalModule); | |||||
| objectMapper.registerModule(bigIntegerModule); | |||||
| jackson2HttpMessageConverter.setObjectMapper(objectMapper); | |||||
| converters.add(jackson2HttpMessageConverter); | |||||
| } | |||||
| @Bean | |||||
| public FilterRegistrationBean<HttpServletRequestWrapperFilter> Filters() { | |||||
| FilterRegistrationBean<HttpServletRequestWrapperFilter> registrationBean = new FilterRegistrationBean<HttpServletRequestWrapperFilter>(); | |||||
| registrationBean.setFilter(new HttpServletRequestWrapperFilter()); | |||||
| registrationBean.addUrlPatterns("/*"); | |||||
| registrationBean.setName("koalaSignFilter"); | |||||
| return registrationBean; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,33 @@ | |||||
| package com.iformall.config; | |||||
| import me.chanjar.weixin.mp.api.WxMpConfigStorage; | |||||
| import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage; | |||||
| import me.chanjar.weixin.mp.api.WxMpService; | |||||
| import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.context.annotation.Bean; | |||||
| import org.springframework.stereotype.Component; | |||||
| @Component | |||||
| public class WechatMpConfig { | |||||
| @Autowired | |||||
| private WechatWebProperties wechatWebProperties; | |||||
| @Bean | |||||
| public WxMpService wxMpService() { | |||||
| //创建WxMpService实例并设置appid和sectret | |||||
| WxMpService wxMpService = new WxMpServiceImpl(); | |||||
| //这里的设置方式是跟着这个sdk的文档写的 | |||||
| wxMpService.setWxMpConfigStorage(wxConfigProvider()); | |||||
| return wxMpService; | |||||
| } | |||||
| @Bean | |||||
| public WxMpConfigStorage wxConfigProvider(){ | |||||
| WxMpInMemoryConfigStorage wxConfigProvider = new WxMpInMemoryConfigStorage(); | |||||
| wxConfigProvider.setAppId(wechatWebProperties.getAppId()); | |||||
| wxConfigProvider.setSecret(wechatWebProperties.getSecret()); | |||||
| return wxConfigProvider; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,59 @@ | |||||
| package com.iformall.config; | |||||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||||
| import org.springframework.boot.context.properties.ConfigurationProperties; | |||||
| import org.springframework.stereotype.Component; | |||||
| /** | |||||
| * Stormeye | |||||
| */ | |||||
| @Component | |||||
| @ConfigurationProperties(prefix = "wechat.web") | |||||
| public class WechatWebProperties { | |||||
| /** | |||||
| * 设置微信第三方平台-微信登录的web应用appid | |||||
| */ | |||||
| private String appId; | |||||
| /** | |||||
| * 设置微信第三方平台-微信登录的web应用app secret | |||||
| */ | |||||
| private String secret; | |||||
| /** | |||||
| * 网页URL | |||||
| * @return | |||||
| */ | |||||
| private String url; | |||||
| public String getAppId() { | |||||
| return appId; | |||||
| } | |||||
| public void setAppId(String appId) { | |||||
| this.appId = appId; | |||||
| } | |||||
| public String getSecret() { | |||||
| return secret; | |||||
| } | |||||
| public void setSecret(String secret) { | |||||
| this.secret = secret; | |||||
| } | |||||
| public String getUrl() { | |||||
| return url; | |||||
| } | |||||
| public void setUrl(String url) { | |||||
| this.url = url; | |||||
| } | |||||
| @Override | |||||
| public String toString() { | |||||
| return ToStringBuilder.reflectionToString(this, | |||||
| ToStringStyle.MULTI_LINE_STYLE); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,46 @@ | |||||
| package com.iformall.controller.base; | |||||
| import com.iformall.utils.IPUtil; | |||||
| import org.springframework.web.bind.WebDataBinder; | |||||
| import org.springframework.web.bind.annotation.InitBinder; | |||||
| import org.springframework.web.bind.annotation.RestController; | |||||
| import org.springframework.web.context.request.RequestContextHolder; | |||||
| import org.springframework.web.context.request.ServletRequestAttributes; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import java.beans.PropertyEditorSupport; | |||||
| import java.text.ParseException; | |||||
| import java.text.SimpleDateFormat; | |||||
| import java.util.Date; | |||||
| @RestController | |||||
| public class BaseController { | |||||
| @InitBinder | |||||
| public void InitBinder(WebDataBinder dataBinder) { | |||||
| dataBinder.registerCustomEditor(Date.class, new PropertyEditorSupport() { | |||||
| public void setAsText(String value) { | |||||
| try { | |||||
| setValue(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(value)); | |||||
| } catch(ParseException e) { | |||||
| try { | |||||
| setValue(new SimpleDateFormat("yyyy-MM-dd ").parse(value)); | |||||
| } catch (ParseException e1) { | |||||
| setValue(null); | |||||
| } | |||||
| } | |||||
| } | |||||
| public String getAsText() { | |||||
| return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) getValue()); | |||||
| } | |||||
| }); | |||||
| } | |||||
| public String getIpAddr() { | |||||
| HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); | |||||
| String ipaddress = IPUtil.getIpAddr(request); | |||||
| return ipaddress; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,48 @@ | |||||
| package com.iformall.controller.callback; | |||||
| import com.iformall.controller.base.BaseController; | |||||
| import com.iformall.service.WxMsgCallbackService; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.web.bind.annotation.*; | |||||
| import java.util.Map; | |||||
| @RestController | |||||
| @RequestMapping("wxMsgCallback") | |||||
| public class WxMsgCallbackController extends BaseController { | |||||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||||
| @Autowired | |||||
| private WxMsgCallbackService wxMsgCallbackService; | |||||
| @PostMapping(value = "/receivemsg/{tenantId}") | |||||
| public void receivemsg(@PathVariable String tenantId, @RequestParam Map<String, String> param) { | |||||
| logger.debug("[" + getIpAddr() + "] WxMsgCallbackController::receivemsg"); | |||||
| logger.info(param.toString()); | |||||
| //解析param数据插入数据库中 | |||||
| wxMsgCallbackService.saveOrUpdate(tenantId,param); | |||||
| } | |||||
| @RequestMapping(value = "/receivemodel/{tenantId}") | |||||
| public void receivemodel(@PathVariable String tenantId, @RequestParam Map<String, String> param) { | |||||
| logger.debug("[" + getIpAddr() + "] WxMsgCallbackController::receivemodel"); | |||||
| logger.info(param.toString()); | |||||
| //解析param数据插入数据库中 | |||||
| wxMsgCallbackService.receivemodel(tenantId, param); | |||||
| } | |||||
| @RequestMapping(value = "/receiveverifymodel/{tenantId}") | |||||
| public void receiveverifymodel(@PathVariable String tenantId, @RequestParam Map<String, String> param) { | |||||
| logger.debug("[" + getIpAddr() + "] WxMsgCallbackController::receiveverifymodel"); | |||||
| logger.info(param.toString()); | |||||
| //解析param数据插入数据库中 | |||||
| wxMsgCallbackService.receiveverifymodel(tenantId, param); | |||||
| } | |||||
| } | |||||
| @@ -13,9 +13,11 @@ import org.apache.commons.io.IOUtils; | |||||
| import org.jdom2.JDOMException; | import org.jdom2.JDOMException; | ||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
| import org.springframework.http.MediaType; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.web.bind.annotation.*; | |||||
| import org.springframework.http.MediaType; | |||||
| import org.springframework.web.bind.annotation.RequestMapping; | |||||
| import org.springframework.web.bind.annotation.ResponseBody; | |||||
| import org.springframework.web.bind.annotation.RestController; | |||||
| import javax.servlet.http.HttpServletRequest; | import javax.servlet.http.HttpServletRequest; | ||||
| import java.io.ByteArrayOutputStream; | import java.io.ByteArrayOutputStream; | ||||
| @@ -0,0 +1,36 @@ | |||||
| package com.iformall.controller.official; | |||||
| import com.iformall.common.ResultData; | |||||
| import com.iformall.controller.base.BaseController; | |||||
| import com.iformall.domain.po.WxMsgValidationcode; | |||||
| import com.iformall.service.WxMallApplyService; | |||||
| import io.swagger.annotations.ApiImplicitParam; | |||||
| import io.swagger.annotations.ApiImplicitParams; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.web.bind.annotation.*; | |||||
| @RestController | |||||
| @RequestMapping("wxMallApply") | |||||
| public class WxMallApplyController extends BaseController { | |||||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||||
| @Autowired | |||||
| private WxMallApplyService wxMallApplyService; | |||||
| @GetMapping("/sendvalidationcode") | |||||
| @ApiImplicitParams({ | |||||
| @ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true), | |||||
| @ApiImplicitParam(name = "type", value = "场景", dataType = "Integer", paramType = "query", required = true)}) | |||||
| public ResultData sendvalidationcode(String phone, Integer type) { | |||||
| logger.debug("[" + getIpAddr() + "] WxMallApplyController::sendvalidationcode"); | |||||
| WxMsgValidationcode wxMsgValidationcode = new WxMsgValidationcode(); | |||||
| wxMsgValidationcode.setTenantId("1"); | |||||
| wxMsgValidationcode.setPhone(phone); | |||||
| wxMsgValidationcode.setType(type); | |||||
| return wxMallApplyService.sendvalidationcode(wxMsgValidationcode); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,76 @@ | |||||
| package com.iformall.interceptor; | |||||
| import javax.servlet.ReadListener; | |||||
| import javax.servlet.ServletInputStream; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import java.io.*; | |||||
| public class BodyReaderHttpServletRequestWrapper extends XssHttpServletRequestWrapper { | |||||
| private final String body; | |||||
| public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { | |||||
| super(request); | |||||
| StringBuffer stringBuilder = new StringBuffer(); | |||||
| BufferedReader bufferedReader = null; | |||||
| try { | |||||
| InputStream inputStream = request.getInputStream(); | |||||
| if (inputStream != null) { | |||||
| bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"utf-8")); | |||||
| char[] charBuffer = new char[1024]; | |||||
| int bytesRead = -1; | |||||
| while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { | |||||
| stringBuilder.append(charBuffer, 0, bytesRead); | |||||
| } | |||||
| } else { | |||||
| stringBuilder.append(""); | |||||
| } | |||||
| } catch (IOException ex) { | |||||
| throw ex; | |||||
| } finally { | |||||
| if (bufferedReader != null) { | |||||
| try { | |||||
| bufferedReader.close(); | |||||
| } catch (IOException ex) { | |||||
| throw ex; | |||||
| } | |||||
| } | |||||
| } | |||||
| body = stringBuilder.toString(); | |||||
| } | |||||
| @Override | |||||
| public ServletInputStream getInputStream() throws IOException { | |||||
| final ByteArrayInputStream byteArrayInputStream = | |||||
| new ByteArrayInputStream(body.getBytes("utf-8")); | |||||
| return new ServletInputStream() { | |||||
| @Override | |||||
| public boolean isFinished() { | |||||
| return false; | |||||
| } | |||||
| @Override | |||||
| public boolean isReady() { | |||||
| return false; | |||||
| } | |||||
| @Override | |||||
| public void setReadListener(ReadListener readListener) { | |||||
| } | |||||
| @Override | |||||
| public int read() throws IOException { | |||||
| return byteArrayInputStream.read(); | |||||
| } | |||||
| }; | |||||
| } | |||||
| @Override | |||||
| public BufferedReader getReader() throws IOException { | |||||
| return new BufferedReader(new InputStreamReader(this.getInputStream())); | |||||
| } | |||||
| public String getBody() { | |||||
| return this.body; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,72 @@ | |||||
| package com.iformall.interceptor; | |||||
| import com.iformall.utils.UrlCheck; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import javax.servlet.*; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import javax.servlet.http.HttpServletResponse; | |||||
| import java.io.IOException; | |||||
| import java.util.Arrays; | |||||
| public class HttpServletRequestWrapperFilter implements Filter { | |||||
| private static final Logger logger = LoggerFactory.getLogger(HttpServletRequestWrapperFilter.class); | |||||
| // 多个跨域域名设置 | |||||
| // public static final String[] ALLOW_DOMAIN = {"https://admin.malls.iformall.com"}; | |||||
| @Override | |||||
| public void init(FilterConfig filterConfig) throws ServletException { | |||||
| } | |||||
| @Override | |||||
| public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | |||||
| throws IOException, ServletException { | |||||
| ServletRequest requestWrapper = null; | |||||
| /* // 跨域访问 | |||||
| HttpServletRequest req = (HttpServletRequest) request; | |||||
| HttpServletResponse res = (HttpServletResponse) response; | |||||
| String originHeader = req.getHeader("Origin"); | |||||
| if (Arrays.asList(ALLOW_DOMAIN).contains(originHeader)) { | |||||
| //通过在响应 header 中设置 ‘*’ 来允许来自所有域的跨域请求访问。 | |||||
| res.setHeader("Access-Control-Allow-Origin", originHeader); | |||||
| //通过对 Credentials 参数的设置,就可以保持跨域 Ajax 时的 Cookie | |||||
| //设置了Allow-Credentials,Allow-Origin就不能为*,需要指明具体的url域 | |||||
| res.setHeader("Access-Control-Allow-Credentials", "true"); | |||||
| //请求方式 | |||||
| res.setHeader("Access-Control-Allow-Methods", "*"); | |||||
| //(预检请求)的返回结果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息) 可以被缓存多久 | |||||
| res.setHeader("Access-Control-Max-Age", "86400"); | |||||
| //首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段 | |||||
| //res.setHeader("Access-Control-Allow-Headers", "*"); | |||||
| res.setHeader("Access-Control-Allow-Headers", | |||||
| "Timestamp,Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token,Access-Control-Allow-Headers"); | |||||
| } | |||||
| */ | |||||
| //sql,xss过滤 | |||||
| XssHttpServletRequestWrapper xssHttpServletRequestWrapper = new XssHttpServletRequestWrapper((HttpServletRequest)request); | |||||
| if (request instanceof HttpServletRequest) { | |||||
| String url = ((HttpServletRequest) request).getRequestURI(); | |||||
| if (!UrlCheck.checkUrl(url)) { | |||||
| requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request); | |||||
| } | |||||
| } | |||||
| if (null == requestWrapper) { | |||||
| chain.doFilter(xssHttpServletRequestWrapper, response); | |||||
| } else { | |||||
| chain.doFilter(requestWrapper, response); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void destroy() { | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,117 @@ | |||||
| package com.iformall.interceptor; | |||||
| import com.iformall.common.ErrorCode; | |||||
| import com.iformall.exception.MallinkException; | |||||
| import com.iformall.utils.HashUtil; | |||||
| import com.iformall.utils.IPUtil; | |||||
| import com.iformall.utils.UrlCheck; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.dao.DataAccessException; | |||||
| import org.springframework.data.redis.connection.RedisConnection; | |||||
| import org.springframework.data.redis.core.RedisCallback; | |||||
| import org.springframework.data.redis.core.RedisTemplate; | |||||
| import org.springframework.data.redis.serializer.RedisSerializer; | |||||
| import org.springframework.stereotype.Component; | |||||
| import org.springframework.web.servlet.ModelAndView; | |||||
| import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; | |||||
| import redis.clients.jedis.Protocol; | |||||
| import redis.clients.util.SafeEncoder; | |||||
| import javax.annotation.Resource; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import javax.servlet.http.HttpServletResponse; | |||||
| import java.io.ByteArrayOutputStream; | |||||
| import java.io.InputStream; | |||||
| import java.nio.charset.Charset; | |||||
| import java.util.Enumeration; | |||||
| import java.util.concurrent.TimeUnit; | |||||
| /** | |||||
| * 幂等检查 | |||||
| * @author stormeye.wu | |||||
| * @email wuguoqiang@iformall.com | |||||
| * @date 2017-03-23 15:38 | |||||
| */ | |||||
| @Component | |||||
| public class RequestInterceptor extends HandlerInterceptorAdapter { | |||||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||||
| @Resource | |||||
| private RedisTemplate<String, String> redisTemplate; | |||||
| @Override | |||||
| public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { | |||||
| if ("GET".equalsIgnoreCase(request.getMethod())) { | |||||
| // 获取不检查幂等 | |||||
| return true; | |||||
| } | |||||
| String ipaddress = IPUtil.getIpAddr(request); | |||||
| String url = request.getRequestURL().toString(); | |||||
| if (UrlCheck.checkUrl(url)) { | |||||
| // pvlog不检查幂等 | |||||
| // awsFileUpload不检查幂等 | |||||
| // ueditor 不检查幂等 | |||||
| return true; | |||||
| } | |||||
| StringBuilder sb = new StringBuilder(); | |||||
| sb.append(url); | |||||
| sb.append("method=").append(request.getMethod()).append("&"); | |||||
| sb.append("ip=").append(ipaddress).append("&"); | |||||
| final Enumeration parameterNames = request.getParameterNames(); | |||||
| while (parameterNames.hasMoreElements()) { | |||||
| String key = (String) parameterNames.nextElement(); | |||||
| if(key.equalsIgnoreCase("ran")) // 跳过ran | |||||
| continue; | |||||
| String parameter = request.getParameter(key); | |||||
| sb.append(key).append("=").append(parameter).append("&"); | |||||
| } | |||||
| InputStream inStream = request.getInputStream(); | |||||
| ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); | |||||
| byte[] buffer = new byte[1024]; | |||||
| int len = 0; | |||||
| while ((len = inStream.read(buffer)) != -1) { | |||||
| outSteam.write(buffer, 0, len); | |||||
| } | |||||
| String resultBody = new String(outSteam.toByteArray(), Charset.forName("UTF-8")); | |||||
| inStream.close(); | |||||
| outSteam.close(); | |||||
| sb.append(resultBody); | |||||
| String key = "request:A:" + HashUtil.md5(sb.toString()); | |||||
| Boolean isAbsent = redisTemplate.<Boolean>execute(new RedisCallback<Boolean>() { | |||||
| @Override | |||||
| public Boolean doInRedis(RedisConnection connection) throws DataAccessException { | |||||
| RedisSerializer valueSerializer = redisTemplate.getValueSerializer(); | |||||
| RedisSerializer keySerializer = redisTemplate.getKeySerializer(); | |||||
| Object obj = connection.execute("set", keySerializer.serialize(key), | |||||
| valueSerializer.serialize(key), | |||||
| SafeEncoder.encode("NX"), | |||||
| SafeEncoder.encode("EX"), | |||||
| Protocol.toByteArray(3)); // 3s | |||||
| return obj != null; | |||||
| } | |||||
| }); | |||||
| if (isAbsent) { | |||||
| logger.info(key + ": 第一次提交"); | |||||
| return true; | |||||
| } | |||||
| logger.info(key + ": 第二次提交"); | |||||
| throw new MallinkException(ErrorCode.SYS_REPEAT_SUBMIT_EXCEPTION); | |||||
| } | |||||
| @Override | |||||
| public void postHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { | |||||
| } | |||||
| @Override | |||||
| public void afterCompletion(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, Exception ex) throws Exception { | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,125 @@ | |||||
| package com.iformall.interceptor; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.util.StringUtils; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import javax.servlet.http.HttpServletRequestWrapper; | |||||
| import java.util.HashMap; | |||||
| import java.util.HashSet; | |||||
| import java.util.Map; | |||||
| import java.util.Set; | |||||
| /** | |||||
| * 防止sql注入,xss攻击 | |||||
| * 前端可以对输入信息做预处理,后端也可以做处理。 | |||||
| */ | |||||
| public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { | |||||
| private final Logger log = LoggerFactory.getLogger(getClass()); | |||||
| private static String key = "and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+"; | |||||
| private static Set<String> notAllowedKeyWords = new HashSet<String>(0); | |||||
| private static String replacedString="INVALID"; | |||||
| static { | |||||
| String keyStr[] = key.split("\\|"); | |||||
| for (String str : keyStr) { | |||||
| notAllowedKeyWords.add(str); | |||||
| } | |||||
| } | |||||
| private String currentUrl; | |||||
| public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) { | |||||
| super(servletRequest); | |||||
| currentUrl = servletRequest.getRequestURI(); | |||||
| } | |||||
| /**覆盖getParameter方法,将参数名和参数值都做xss过滤。 | |||||
| * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取 | |||||
| * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖 | |||||
| */ | |||||
| @Override | |||||
| public String getParameter(String parameter) { | |||||
| String value = super.getParameter(parameter); | |||||
| if (value == null) { | |||||
| return null; | |||||
| } | |||||
| return cleanXSS(value); | |||||
| } | |||||
| @Override | |||||
| public String[] getParameterValues(String parameter) { | |||||
| String[] values = super.getParameterValues(parameter); | |||||
| if (values == null) { | |||||
| return null; | |||||
| } | |||||
| int count = values.length; | |||||
| String[] encodedValues = new String[count]; | |||||
| for (int i = 0; i < count; i++) { | |||||
| encodedValues[i] = cleanXSS(values[i]); | |||||
| } | |||||
| return encodedValues; | |||||
| } | |||||
| @Override | |||||
| public Map<String, String[]> getParameterMap(){ | |||||
| Map<String, String[]> values=super.getParameterMap(); | |||||
| if (values == null) { | |||||
| return null; | |||||
| } | |||||
| Map<String, String[]> result=new HashMap<>(); | |||||
| for(String key:values.keySet()){ | |||||
| String encodedKey=cleanXSS(key); | |||||
| int count=values.get(key).length; | |||||
| String[] encodedValues = new String[count]; | |||||
| for (int i = 0; i < count; i++){ | |||||
| encodedValues[i]=cleanXSS(values.get(key)[i]); | |||||
| } | |||||
| result.put(encodedKey,encodedValues); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| /** | |||||
| * 覆盖getHeader方法,将参数名和参数值都做xss过滤。 | |||||
| * 如果需要获得原始的值,则通过super.getHeaders(name)来获取 | |||||
| * getHeaderNames 也可能需要覆盖 | |||||
| */ | |||||
| @Override | |||||
| public String getHeader(String name) { | |||||
| if(name.equalsIgnoreCase("user-agent")) { | |||||
| return super.getHeader(name); | |||||
| } | |||||
| String value = super.getHeader(name); | |||||
| if (value == null) { | |||||
| return null; | |||||
| } | |||||
| return cleanXSS(value); | |||||
| } | |||||
| private String cleanXSS(String valueP) { | |||||
| // You'll need to remove the spaces from the html entities below | |||||
| String value = valueP.replaceAll("<", "<").replaceAll(">", ">"); | |||||
| value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;"); | |||||
| value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;"); | |||||
| value = value.replaceAll("'", "& #39;"); | |||||
| value = value.replaceAll("eval\\((.*)\\)", ""); | |||||
| value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\""); | |||||
| value = value.replaceAll("script", ""); | |||||
| value = cleanSqlKeyWords(value); | |||||
| return value; | |||||
| } | |||||
| private String cleanSqlKeyWords(String value) { | |||||
| String paramValue = value; | |||||
| for (String keyword : notAllowedKeyWords) { | |||||
| if (paramValue.length() > keyword.length() + 4 | |||||
| && (paramValue.contains(" "+keyword)||paramValue.contains(keyword+" ")||paramValue.contains(" "+keyword+" "))) { | |||||
| paramValue = StringUtils.replace(paramValue, keyword, replacedString); | |||||
| log.error(this.currentUrl + "已被过滤,因为参数中包含不允许sql的关键词(" + keyword | |||||
| + ")"+";参数:"+value+";过滤后的参数:"+paramValue); | |||||
| } | |||||
| } | |||||
| return paramValue; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,45 @@ | |||||
| package com.iformall.shiro; | |||||
| import com.iformall.common.ErrorCode; | |||||
| import com.iformall.domain.po.MallUserInfo; | |||||
| import com.iformall.enums.EnumLoginType; | |||||
| import com.iformall.enums.EnumMallUserStatus; | |||||
| import com.iformall.service.MallUserInfoService; | |||||
| import org.apache.shiro.authc.AuthenticationInfo; | |||||
| import org.apache.shiro.authc.AuthenticationToken; | |||||
| import org.apache.shiro.authc.DisabledAccountException; | |||||
| import org.apache.shiro.authc.UnknownAccountException; | |||||
| import org.apache.shiro.authc.credential.HashedCredentialsMatcher; | |||||
| import org.springframework.context.annotation.Configuration; | |||||
| import javax.annotation.Resource; | |||||
| @Configuration | |||||
| public class MyRetryLimitCredentialsMatcher extends HashedCredentialsMatcher { | |||||
| @Resource | |||||
| private MallUserInfoService userService; | |||||
| @Override | |||||
| public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) { | |||||
| if(authcToken instanceof UseriFormallToken) { | |||||
| UseriFormallToken tk = (UseriFormallToken) authcToken; | |||||
| if(tk.getType().equals(EnumLoginType.NOPASSWD)){ | |||||
| // 获取用户的输入的账号. | |||||
| String username = (String)tk.getPrincipal(); | |||||
| MallUserInfo user = userService.getByUsername(username); | |||||
| if(user == null) { | |||||
| throw new UnknownAccountException(ErrorCode.USER_IS_EMPTY.getMessage()); | |||||
| } | |||||
| // 用户被禁用 | |||||
| if(user.getStatus()==null ||!EnumMallUserStatus.VALID.getCode().equals(user.getStatus())) { | |||||
| throw new DisabledAccountException(ErrorCode.USER_IS_LOCKED.getMessage()); | |||||
| } | |||||
| return true; | |||||
| } | |||||
| boolean matches = super.doCredentialsMatch(authcToken, info); | |||||
| return matches; | |||||
| } | |||||
| boolean matches =super.doCredentialsMatch(authcToken, info); | |||||
| return matches; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,79 @@ | |||||
| package com.iformall.shiro; | |||||
| import com.iformall.common.ErrorCode; | |||||
| import com.iformall.domain.po.MallUserInfo; | |||||
| import com.iformall.enums.EnumMallUserStatus; | |||||
| import com.iformall.service.MallUserInfoService; | |||||
| import org.apache.shiro.SecurityUtils; | |||||
| import org.apache.shiro.authc.*; | |||||
| import org.apache.shiro.authz.AuthorizationInfo; | |||||
| import org.apache.shiro.authz.SimpleAuthorizationInfo; | |||||
| import org.apache.shiro.realm.AuthorizingRealm; | |||||
| import org.apache.shiro.session.Session; | |||||
| import org.apache.shiro.subject.PrincipalCollection; | |||||
| import org.apache.shiro.util.ByteSource; | |||||
| import javax.annotation.Resource; | |||||
| import java.util.Set; | |||||
| /** | |||||
| * Created by yangqj on 2017/4/21. | |||||
| */ | |||||
| public class MyShiroRealm extends AuthorizingRealm { | |||||
| @Resource | |||||
| private MallUserInfoService userService; | |||||
| //授权 | |||||
| @Override | |||||
| protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { | |||||
| MallUserInfo user= (MallUserInfo) SecurityUtils.getSubject().getPrincipal(); | |||||
| Set<String> permissionSet = userService.getUserPermissions(user); | |||||
| SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); | |||||
| info.setStringPermissions(permissionSet); | |||||
| return info; | |||||
| } | |||||
| //认证 | |||||
| @Override | |||||
| protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { | |||||
| //获取用户的输入的账号. | |||||
| String username = (String)token.getPrincipal(); | |||||
| MallUserInfo user = userService.getByUsername(username); | |||||
| if(user == null) { | |||||
| throw new UnknownAccountException(ErrorCode.USER_IS_EMPTY.getMessage()); | |||||
| } | |||||
| if(user.getTenantId().equals("1")) { | |||||
| // 只支持租户为1的用户 | |||||
| throw new UnknownAccountException("租户不支持"); | |||||
| } | |||||
| if(user.getStatus()==null || | |||||
| !EnumMallUserStatus.VALID.getCode().equals(user.getStatus())) {//用户被禁用 | |||||
| throw new DisabledAccountException(ErrorCode.USER_IS_LOCKED.getMessage()); | |||||
| } | |||||
| SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( | |||||
| user, //用户 | |||||
| user.getPassword(), //密码 | |||||
| ByteSource.Util.bytes(username), | |||||
| getName() //realm name | |||||
| ); | |||||
| // 当验证都通过后,把用户信息放在session里 | |||||
| Session session = SecurityUtils.getSubject().getSession(); | |||||
| session.setAttribute(UserSession.userInfo, user); | |||||
| session.setAttribute(UserSession.userId, user.getId()); | |||||
| session.setAttribute(UserSession.tenantId, user.getTenantId()); | |||||
| return authenticationInfo; | |||||
| } | |||||
| /** | |||||
| * 指定principalCollection 清除 | |||||
| */ | |||||
| /* public void clearCachedAuthorizationInfo(PrincipalCollection principalCollection) { | |||||
| SimplePrincipalCollection principals = new SimplePrincipalCollection( | |||||
| principalCollection, getName()); | |||||
| super.clearCachedAuthorizationInfo(principals); | |||||
| } | |||||
| */ | |||||
| } | |||||
| @@ -0,0 +1,32 @@ | |||||
| package com.iformall.shiro; | |||||
| import com.iformall.domain.po.MallUserInfo; | |||||
| import org.apache.shiro.crypto.hash.SimpleHash; | |||||
| import org.apache.shiro.util.ByteSource; | |||||
| public class PasswordHelper { | |||||
| //private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator(); | |||||
| private String algorithmName = "md5"; | |||||
| private int hashIterations = 2; | |||||
| public void encryptPassword(MallUserInfo user) { | |||||
| //String salt=randomNumberGenerator.nextBytes().toHex(); | |||||
| String newPassword = new SimpleHash(algorithmName, user.getPassword(), ByteSource.Util.bytes(user.getUsername()), hashIterations).toHex(); | |||||
| //String newPassword = new SimpleHash(algorithmName, user.getPassword()).toHex(); | |||||
| user.setPassword(newPassword); | |||||
| } | |||||
| public static void main(String[] args) { | |||||
| MallUserInfo user = new MallUserInfo(); | |||||
| user.setUsername("fmkjadmin"); | |||||
| user.setPassword("fmkjadmin790"); | |||||
| PasswordHelper passwordHelper = new PasswordHelper(); | |||||
| passwordHelper.encryptPassword(user); | |||||
| System.out.println(user); | |||||
| System.out.println(user.getPassword()); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,126 @@ | |||||
| package com.iformall.shiro; | |||||
| import com.iformall.service.MallPermissionService; | |||||
| import org.apache.shiro.spring.web.ShiroFilterFactoryBean; | |||||
| import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager; | |||||
| import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver; | |||||
| import org.apache.shiro.web.servlet.AbstractShiroFilter; | |||||
| import org.crazycake.shiro.RedisSessionDAO; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import java.util.LinkedHashMap; | |||||
| import java.util.Map; | |||||
| /** | |||||
| * Created by yangqj on 2017/4/30. | |||||
| */ | |||||
| //@Service | |||||
| public class ShiroService { | |||||
| @Autowired | |||||
| private ShiroFilterFactoryBean shiroFilterFactoryBean; | |||||
| @Autowired | |||||
| private MallPermissionService resourcesService; | |||||
| @Autowired | |||||
| private RedisSessionDAO redisSessionDAO; | |||||
| /** | |||||
| * 初始化权限 | |||||
| */ | |||||
| public Map<String, String> loadFilterChainDefinitions() { | |||||
| // 权限控制map.从数据库获取 | |||||
| Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); | |||||
| filterChainDefinitionMap.put("/logout", "logout"); | |||||
| filterChainDefinitionMap.put("/css/**","anon"); | |||||
| filterChainDefinitionMap.put("/js/**","anon"); | |||||
| filterChainDefinitionMap.put("/img/**","anon"); | |||||
| filterChainDefinitionMap.put("/user/**","anon"); | |||||
| filterChainDefinitionMap.put("/font-awesome/**","anon"); | |||||
| // Map<String,Object> map = new HashMap<>(); | |||||
| // List<SysPermission> resourcesList = resourcesService.list(map); | |||||
| // for(SysPermission resources:resourcesList){ | |||||
| // | |||||
| // if (StringUtil.isNotEmpty(resources.getUrl())) { | |||||
| // String permission = "perms[" + resources.getUrl()+ "]"; | |||||
| // filterChainDefinitionMap.put(resources.getUrl(),permission); | |||||
| // } | |||||
| // } | |||||
| filterChainDefinitionMap.put("/**", "authc"); | |||||
| return filterChainDefinitionMap; | |||||
| } | |||||
| /** | |||||
| * 重新加载权限 | |||||
| */ | |||||
| public void updatePermission() { | |||||
| synchronized (shiroFilterFactoryBean) { | |||||
| AbstractShiroFilter shiroFilter = null; | |||||
| try { | |||||
| shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean | |||||
| .getObject(); | |||||
| } catch (Exception e) { | |||||
| throw new RuntimeException( | |||||
| "get ShiroFilter from shiroFilterFactoryBean error!"); | |||||
| } | |||||
| PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter | |||||
| .getFilterChainResolver(); | |||||
| DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver | |||||
| .getFilterChainManager(); | |||||
| // 清空老的权限控制 | |||||
| manager.getFilterChains().clear(); | |||||
| shiroFilterFactoryBean.getFilterChainDefinitionMap().clear(); | |||||
| shiroFilterFactoryBean | |||||
| .setFilterChainDefinitionMap(loadFilterChainDefinitions()); | |||||
| // 重新构建生成 | |||||
| Map<String, String> chains = shiroFilterFactoryBean | |||||
| .getFilterChainDefinitionMap(); | |||||
| for (Map.Entry<String, String> entry : chains.entrySet()) { | |||||
| String url = entry.getKey(); | |||||
| String chainDefinition = entry.getValue().trim() | |||||
| .replace(" ", ""); | |||||
| manager.createChain(url, chainDefinition); | |||||
| } | |||||
| System.out.println("更新权限成功!!"); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * 根据userId 清除当前session存在的用户的权限缓存 | |||||
| * @param userIds 已经修改了权限的userId | |||||
| */ | |||||
| /* public void clearUserAuthByUserId(List<Integer> userIds){ | |||||
| if(null == userIds || userIds.size() == 0) return ; | |||||
| //获取所有session | |||||
| Collection<Session> sessions = redisSessionDAO.getActiveSessions(); | |||||
| //定义返回 | |||||
| List<SimplePrincipalCollection> list = new ArrayList<SimplePrincipalCollection>(); | |||||
| for (Session session:sessions){ | |||||
| //获取session登录信息。 | |||||
| Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY); | |||||
| if(null != obj && obj instanceof SimplePrincipalCollection){ | |||||
| //强转 | |||||
| SimplePrincipalCollection spc = (SimplePrincipalCollection)obj; | |||||
| //判断用户,匹配用户ID。 | |||||
| obj = spc.getPrimaryPrincipal(); | |||||
| if(null != obj && obj instanceof User){ | |||||
| User user = (User) obj; | |||||
| System.out.println("user:"+user); | |||||
| //比较用户ID,符合即加入集合 | |||||
| if(null != user && userIds.contains(user.getId())){ | |||||
| list.add(spc); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| RealmSecurityManager securityManager = | |||||
| (RealmSecurityManager) SecurityUtils.getSecurityManager(); | |||||
| MyShiroRealm realm = (MyShiroRealm)securityManager.getRealms().iterator().next(); | |||||
| for (SimplePrincipalCollection simplePrincipalCollection : list) { | |||||
| realm.clearCachedAuthorizationInfo(simplePrincipalCollection); | |||||
| } | |||||
| }*/ | |||||
| } | |||||
| @@ -0,0 +1,11 @@ | |||||
| package com.iformall.shiro; | |||||
| public class UserSession { | |||||
| public static String userInfo="userSession"; | |||||
| public static String userId ="userSessionId"; | |||||
| public static String tenantId ="TENANT_ID"; | |||||
| } | |||||
| @@ -0,0 +1,39 @@ | |||||
| package com.iformall.shiro; | |||||
| import com.iformall.enums.EnumLoginType; | |||||
| import org.apache.shiro.authc.UsernamePasswordToken; | |||||
| public class UseriFormallToken extends UsernamePasswordToken { | |||||
| private static final long serialVersionUID = -2564928913725078138L; | |||||
| private EnumLoginType type; | |||||
| public UseriFormallToken() { | |||||
| super(); | |||||
| } | |||||
| public UseriFormallToken(String username, String password, EnumLoginType type, boolean rememberMe, String host) { | |||||
| super(username, password, rememberMe, host); | |||||
| this.type = type; | |||||
| } | |||||
| /** 免密登录 */ | |||||
| public UseriFormallToken(String username) { | |||||
| super(username, "", false, null); | |||||
| this.type = EnumLoginType.NOPASSWD; | |||||
| } | |||||
| /** 账号密码登录 */ | |||||
| public UseriFormallToken(String username, String pwd) { | |||||
| super(username, pwd, false, null); | |||||
| this.type = EnumLoginType.PASSWORD; | |||||
| } | |||||
| public EnumLoginType getType() { | |||||
| return type; | |||||
| } | |||||
| public void setType(EnumLoginType type) { | |||||
| this.type = type; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,37 @@ | |||||
| package com.iformall.utils; | |||||
| import com.google.zxing.BarcodeFormat; | |||||
| import com.google.zxing.EncodeHintType; | |||||
| import com.google.zxing.MultiFormatWriter; | |||||
| import com.google.zxing.WriterException; | |||||
| import com.google.zxing.common.BitMatrix; | |||||
| import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; | |||||
| import java.awt.image.BufferedImage; | |||||
| import java.util.Hashtable; | |||||
| import java.util.Map; | |||||
| public class PayUtil { | |||||
| /** | |||||
| * 根据url生成二位图片对象 | |||||
| * | |||||
| * @param codeUrl | |||||
| * @return | |||||
| * @throws WriterException | |||||
| */ | |||||
| public static BufferedImage getQRCodeImge(String codeUrl) throws WriterException { | |||||
| Map<EncodeHintType, Object> hints = new Hashtable(); | |||||
| hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); | |||||
| hints.put(EncodeHintType.CHARACTER_SET, "UTF8"); | |||||
| int width = 256; | |||||
| BitMatrix bitMatrix = (new MultiFormatWriter()).encode(codeUrl, BarcodeFormat.QR_CODE, width, width, hints); | |||||
| BufferedImage image = new BufferedImage(width, width, 1); | |||||
| for(int x = 0; x < width; ++x) { | |||||
| for(int y = 0; y < width; ++y) { | |||||
| image.setRGB(x, y, bitMatrix.get(x, y) ? -16777216 : -1); | |||||
| } | |||||
| } | |||||
| return image; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,22 @@ | |||||
| package com.iformall.utils; | |||||
| /** | |||||
| * @author gongbiao | |||||
| */ | |||||
| public class UrlCheck { | |||||
| public static boolean checkUrl(String url) { | |||||
| return url.contains("awsFileUpload") | |||||
| || url.contains("awsFilesUpload") | |||||
| || url.contains("downQrCode") | |||||
| || url.contains("cimgUpload") | |||||
| || url.contains("ueditor/exec") | |||||
| || url.contains("/wxCUserBasicInfo/importTemplate") | |||||
| || url.contains("/wxCUserBasicInfo/countByFilter") | |||||
| || url.contains("/wxCUserBasicInfo/listByFilter") | |||||
| || url.contains("wxMsgCallback") | |||||
| || url.contains("notify") | |||||
| || url.contains("pvlog"); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,120 @@ | |||||
| spring: | |||||
| profiles: | |||||
| include: rabbitMQ | |||||
| # JDBC | |||||
| datasource: | |||||
| url: jdbc:mysql://202.165.179.86:3306/mallinkDev?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useUnicode=true&useSSL=false&useAffectedRows=true | |||||
| username: ENC(dZ8fmrtuBMQYaRytKQgTqg==) | |||||
| password: ENC(WGu0+1DPIHrqorDhrbq1+7wr7xNG53mN) | |||||
| type: com.alibaba.druid.pool.DruidDataSource | |||||
| driver-class-name: com.mysql.cj.jdbc.Driver | |||||
| filters: stat | |||||
| maxActive: 20 | |||||
| initialSize: 1 | |||||
| maxWait: 60000 | |||||
| minIdle: 1 | |||||
| timeBetweenEvictionRunsMillis: 28000 | |||||
| minEvictableIdleTimeMillis: 28000 | |||||
| validationQuery: select 'x' | |||||
| testWhileIdle: true | |||||
| testOnBorrow: false | |||||
| testOnReturn: false | |||||
| poolPreparedStatements: true | |||||
| maxOpenPreparedStatements: 20 | |||||
| connectionProperties: "druid.stat.mergeSql=true;druid.stat.slowSqlMillis=6000" | |||||
| #jackson: | |||||
| #date-format: yyyy-MM-dd HH:mm:ss | |||||
| # REDIS | |||||
| redis: | |||||
| host: 202.165.179.86 | |||||
| port: 6379 | |||||
| password: ENC(aYJ3Wr2UWtkORRQjjrWWpz2ZeTISsHOA) | |||||
| timeout: 3600 | |||||
| expire: 1800 #30分钟 | |||||
| database: 1 | |||||
| defaultExpiration: 2592000 # 默认生命周期30天 | |||||
| jedis: | |||||
| pool: | |||||
| max-active: 100 | |||||
| max-idle: 500 | |||||
| max-wait: -1 | |||||
| min-idle: 10 | |||||
| mail: | |||||
| host: smtp.exmail.qq.com | |||||
| username: ENC(lknBjZsA24AaQEXuy0fFw3acd4v4Xsf3CsgDcRZgjlYnNAL9R07d/w==) | |||||
| password: ENC(gIkVPuYMmJ/EDxry8QIfGumIAk4plAwolGrfg1fiM3U=) # 授权密码 | |||||
| properties: | |||||
| mail: | |||||
| smtp: | |||||
| auth: true | |||||
| starttls: | |||||
| enable: true | |||||
| socketFactory: | |||||
| port: 465 | |||||
| class: javax.net.ssl.SSLSocketFactory | |||||
| # ROCKETMQ | |||||
| rocketmq: | |||||
| nameServer: 127.0.0.1:9876 | |||||
| producer: | |||||
| retry-times-when-send-async-failed: 0 | |||||
| send-msg-timeout: 300000 | |||||
| compress-msg-body-over-howmuch: 4096 | |||||
| max-message-size: 4194304 | |||||
| retry-another-broker-when-not-store-ok: false | |||||
| retry-times-when-send-failed: 2 | |||||
| # RABBITMQ | |||||
| rabbitmq: | |||||
| host: 202.165.179.86 | |||||
| port: 5672 | |||||
| username: fumao | |||||
| password: f8l89&*%%u7f1t22 | |||||
| publisher-confirms: true | |||||
| virtual-host: / | |||||
| aws: | |||||
| clientRegion: cn-northwest-1 | |||||
| bucketName: iformall-net | |||||
| access: ENC(3gx5ghDFBqGrEhO3Wf8aYmXsnwHO7Cj3HNKJGOeUj0o=) | |||||
| secret: ENC(HVKIJwCJKVXLlUpGlQPwNqJOlnpxn4xYuy91SH0seTSm2uAttIQHvA49fXWWax90v5wloIk0QuU=) | |||||
| wechat: | |||||
| web: | |||||
| appId: "wxe31beafbfd8295ba" | |||||
| secret: "c689fabf3c4c9f5b6424ff2a36a26727" | |||||
| url: "https://mall.youlane.cn" | |||||
| open: | |||||
| componentAppId: "wx897e4673286c915d" | |||||
| componentSecret: "cdfdfda65c45689beb6766c4c427eed2" | |||||
| componentToken: "formall2018" | |||||
| componentAesKey: "htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN" | |||||
| redis: | |||||
| host: 202.165.179.86 | |||||
| port: 6379 | |||||
| password: ENC(aYJ3Wr2UWtkORRQjjrWWpz2ZeTISsHOA) | |||||
| timeout: 3600 | |||||
| expire: 1800 #30分钟 | |||||
| database: 2 | |||||
| defaultExpiration: 2592000 # 默认生命周期30天 | |||||
| jedis: | |||||
| pool: | |||||
| max-active: 100 | |||||
| max-idle: 500 | |||||
| max-wait: -1 | |||||
| min-idle: 10 | |||||
| jasypt: | |||||
| encryptor: | |||||
| password: oRqdnDbK5pj3eMmB | |||||
| fm: | |||||
| exception: false | |||||
| exception_emails: hupeng@iformall.com,wuguoqiang@iformall.com,gongbiao@iformall.com,luozukai@iformall.com,hanxueda@iformall.com | |||||
| deploy: 1 | |||||
| open: true | |||||
| logging: | |||||
| level: | |||||
| tk.mybatis: debug | |||||
| com.iformall: debug | |||||
| path: ./logs/admin | |||||
| @@ -0,0 +1,104 @@ | |||||
| spring: | |||||
| profiles: | |||||
| include: rabbitMQ | |||||
| # JDBC | |||||
| datasource: | |||||
| url: jdbc:mysql://formalldb.cfqyqflkdlit.rds.cn-northwest-1.amazonaws.com.cn:3306/mallink?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false&useAffectedRows=true | |||||
| username: ENC(NUzgQOdJnCbVLKT6BaX0aw==) | |||||
| password: ENC(mvuoDRiu0jqYaKNRwwTuXZ6U7aoIaqsjdiPqTLgi/nY=) | |||||
| type: com.alibaba.druid.pool.DruidDataSource | |||||
| driver-class-name: com.mysql.cj.jdbc.Driver | |||||
| filters: stat | |||||
| maxActive: 20 | |||||
| initialSize: 1 | |||||
| maxWait: 60000 | |||||
| minIdle: 1 | |||||
| timeBetweenEvictionRunsMillis: 28000 | |||||
| minEvictableIdleTimeMillis: 28000 | |||||
| validationQuery: select 'x' | |||||
| testWhileIdle: true | |||||
| testOnBorrow: false | |||||
| testOnReturn: false | |||||
| poolPreparedStatements: true | |||||
| maxOpenPreparedStatements: 20 | |||||
| connectionProperties: "druid.stat.mergeSql=true;druid.stat.slowSqlMillis=6000" | |||||
| # REDIS | |||||
| redis: | |||||
| host: 127.0.0.1 | |||||
| port: 6379 | |||||
| password: ENC(8gYU47Fu93NUJPhwPCiPbAT+6VFA1YDx1egK4Z0Nl6w=) | |||||
| timeout: 3600 | |||||
| expire: 1800 #30分钟 | |||||
| database: 1 | |||||
| defaultExpiration: 2592000 # 默认生命周期30天 | |||||
| jedis: | |||||
| pool: | |||||
| max-active: 100 | |||||
| max-idle: 20 | |||||
| max-wait: -1 | |||||
| min-idle: 0 | |||||
| mail: | |||||
| host: smtp.exmail.qq.com | |||||
| username: ENC(IiL/PHP9wCkpibWHRH/Mts8K9fh4izYqaiaF66bDrqtEHP+KUcJRRg==) | |||||
| password: ENC(m7L57m8mk6tApVoa4XIDQnXF0VxnYjKe4LVdzcgb+xY=) # 授权密码 | |||||
| properties: | |||||
| mail: | |||||
| smtp: | |||||
| auth: true | |||||
| starttls: | |||||
| enable: true | |||||
| socketFactory: | |||||
| port: 465 | |||||
| class: javax.net.ssl.SSLSocketFactory | |||||
| # RABBITMQ | |||||
| rabbitmq: | |||||
| host: localhost | |||||
| port: 5672 | |||||
| username: ENC(lRmLd6EzgeY1RT5ktcHv9g==) | |||||
| password: ENC(gBI8mCjr3OC0v57jcnSb660Ux7mW03K2oePgvohhg7w=) | |||||
| publisher-confirms: true | |||||
| virtual-host: / | |||||
| aws: | |||||
| clientRegion: cn-northwest-1 | |||||
| bucketName: iformall-net | |||||
| access: ENC(a6SN1sZ1enNL49ypiOXkg/pPPAnZD8H4buQFTTKN08s=) | |||||
| secret: ENC(5P5ff4bTMJUbXVR4ZsM03UHzOKZ4+Zg5Iutcdkyp/Quny/oXg+A4KpfwEyGarlLu3vQMJahGP5M=) | |||||
| wechat: | |||||
| web: | |||||
| appId: "wx9cc4ca09eb20fe03" | |||||
| secret: "af1d7f7a1268022a73cb4ce0b9cf0985" | |||||
| url: "https://admin.malls.iformall.com" | |||||
| open: | |||||
| componentAppId: ENC(b3JG0MUZgQfz5Zj+DC2ZM1zDeLOiGTmmeokfe2O8kaM=) | |||||
| componentSecret: ENC(QVyc4BPGdnSjXGs2ivgpDBE1v8wPWHLLRMYI7Vv8uYwC0SiZHQz0QpyyQV/b48Pb) | |||||
| componentToken: ENC(rkxj0733WxFFDLgA9x01m2s5Fi2L+0PC) | |||||
| componentAesKey: ENC(EIbJUBpbYOrLb4YQ/HXLQmxlxgAqIp2ZmpnGICC8pu5xiTz3Cqfkbwd2S8raCcK/IvYcX2GmedI=) | |||||
| redis: | |||||
| host: 127.0.0.1 | |||||
| port: 6379 | |||||
| password: ENC(8gYU47Fu93NUJPhwPCiPbAT+6VFA1YDx1egK4Z0Nl6w=) | |||||
| timeout: 3600 | |||||
| expire: 1800 #30分钟 | |||||
| database: 2 | |||||
| defaultExpiration: 2592000 # 默认生命周期30天 | |||||
| jedis: | |||||
| pool: | |||||
| max-active: 100 | |||||
| max-idle: 100 | |||||
| max-wait: -1 | |||||
| min-idle: 10 | |||||
| fm: | |||||
| exception: true | |||||
| exception_emails: hupeng@iformall.com,wuguoqiang@iformall.com,gongbiao@iformall.com,luozukai@iformall.com,hanxueda@iformall.com | |||||
| deploy: 3 | |||||
| open: true | |||||
| logging: | |||||
| level: | |||||
| tk.mybatis: debug | |||||
| com.iformall.mapper: debug | |||||
| path: ./logs/admin | |||||
| @@ -0,0 +1,104 @@ | |||||
| spring: | |||||
| profiles: | |||||
| include: rabbitMQ | |||||
| # JDBC | |||||
| datasource: | |||||
| url: jdbc:mysql://formalldb.cfqyqflkdlit.rds.cn-northwest-1.amazonaws.com.cn:3306/mallinkTest?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false&useAffectedRows=true | |||||
| username: ENC(Uc0AjgkytxHHCwZrmDASWg==) | |||||
| password: ENC(nV4Mi3bEbBx0Fj7uUyYH55eTaqsFMjKvmNzagicH4pc=) | |||||
| type: com.alibaba.druid.pool.DruidDataSource | |||||
| driver-class-name: com.mysql.cj.jdbc.Driver | |||||
| filters: stat | |||||
| maxActive: 20 | |||||
| initialSize: 1 | |||||
| maxWait: 60000 | |||||
| minIdle: 1 | |||||
| timeBetweenEvictionRunsMillis: 28000 | |||||
| minEvictableIdleTimeMillis: 28000 | |||||
| validationQuery: select 'x' | |||||
| testWhileIdle: true | |||||
| testOnBorrow: false | |||||
| testOnReturn: false | |||||
| poolPreparedStatements: true | |||||
| maxOpenPreparedStatements: 20 | |||||
| connectionProperties: "druid.stat.mergeSql=true;druid.stat.slowSqlMillis=6000" | |||||
| # REDIS | |||||
| redis: | |||||
| host: 127.0.0.1 | |||||
| port: 6379 | |||||
| password: ENC(QFwqv3NshvvGhFPiP8rwhvbnxk+rFSqhJi8Pw6TogSg=) | |||||
| timeout: 3600 | |||||
| expire: 1800 #30分钟 | |||||
| database: 1 | |||||
| defaultExpiration: 2592000 # 默认生命周期30天 | |||||
| jedis: | |||||
| pool: | |||||
| max-active: 100 | |||||
| max-idle: 20 | |||||
| max-wait: -1 | |||||
| min-idle: 0 | |||||
| mail: | |||||
| host: smtp.exmail.qq.com | |||||
| username: ENC(TtJQE2C4cWo8CpVGMMl5hiy5eykepd96J5XjsW3Kj5fgP+6+5ctDNQ==) | |||||
| password: ENC(Zh6A6kRtA2ABtH6nzdvsqQSO1/8p3mparJr8neI2BLU=) # 授权密码 | |||||
| properties: | |||||
| mail: | |||||
| smtp: | |||||
| auth: true | |||||
| starttls: | |||||
| enable: true | |||||
| socketFactory: | |||||
| port: 465 | |||||
| class: javax.net.ssl.SSLSocketFactory | |||||
| # RABBITMQ | |||||
| rabbitmq: | |||||
| host: 127.0.0.1 | |||||
| port: 5672 | |||||
| username: ENC(aSRr6mnSryEqzHHz1hJf1g==) | |||||
| password: ENC(GnjF/mdqKdvmDYC0tIIso7+20/jBALPw39tiWCYJ4iw=) | |||||
| publisher-confirms: true | |||||
| virtual-host: / | |||||
| aws: | |||||
| clientRegion: cn-northwest-1 | |||||
| bucketName: iformall-net | |||||
| access: ENC(NCLcmjwKpAWdn/abD17OKIY7yKepVLWzEpqRYUlURCw=) | |||||
| secret: ENC(TRcZqql0Rq5PExlMeH/4WiZ/i02b8FXKmLTBChJmbluTa1uoLS9LrHyNEMrqe1DK+QgOAdvqGBo=) | |||||
| wechat: | |||||
| web: | |||||
| appId: "wx091907dd0bfd3f6b" | |||||
| secret: "2a2ca10738998b9ef92c1fe8a4d366a6" | |||||
| url: "https://admintest.malls.iformall.com" | |||||
| open: | |||||
| componentAppId: ENC(hnH31XdNzArfMfikKgBFpqQuJUl6rVOPFFcAVyb595o=) | |||||
| componentSecret: ENC(t/GUVX5L+U7LCwSvZ5WmxAnmTMrc/Uy1nljsrokuzzsBL2j6BEVnrS/n7DCdGc/G) | |||||
| componentToken: ENC(gUF1m0YgCCI2IY54fX6sGLZVlCswA8tG) | |||||
| componentAesKey: ENC(TYHcrIkICtfrCfGq4HW6s1b/pHf7OD2uyPN11SzJNB0acqJ/4JVX1nuljo/cAtgMG4O05TImLQc=) | |||||
| redis: | |||||
| host: 127.0.0.1 | |||||
| port: 6379 | |||||
| password: ENC(QFwqv3NshvvGhFPiP8rwhvbnxk+rFSqhJi8Pw6TogSg=) | |||||
| timeout: 3600 | |||||
| expire: 1800 #30分钟 | |||||
| database: 2 | |||||
| defaultExpiration: 2592000 # 默认生命周期30天 | |||||
| jedis: | |||||
| pool: | |||||
| max-active: 100 | |||||
| max-idle: 100 | |||||
| max-wait: -1 | |||||
| min-idle: 10 | |||||
| fm: | |||||
| exception: true | |||||
| exception_emails: hupeng@iformall.com,wuguoqiang@iformall.com,gongbiao@iformall.com,luozukai@iformall.com,hanxueda@iformall.com | |||||
| deploy: 2 | |||||
| open: true | |||||
| logging: | |||||
| level: | |||||
| tk.mybatis: debug | |||||
| com.iformall: debug | |||||
| path: ./logs/admin | |||||
| @@ -0,0 +1,73 @@ | |||||
| server: | |||||
| port: 9100 | |||||
| servlet: | |||||
| context-path: / | |||||
| spring: | |||||
| application: | |||||
| name: mallink | |||||
| profiles: | |||||
| active: dev | |||||
| jackson: | |||||
| date-format: yyyy-MM-dd HH:mm:ss | |||||
| time-zone: GMT+8 | |||||
| default-property-inclusion: non_null | |||||
| servlet: | |||||
| multipart: | |||||
| max-file-size: 2MB | |||||
| max-request-size: 2MB | |||||
| cache: | |||||
| type: REDIS | |||||
| cache-names: redis_cache #缓存的名字(可以不指定) | |||||
| redis: | |||||
| time-to-live: 60000ms #很重要,缓存的有效时间,以便缓存的过期(单位为毫秒) | |||||
| rocketmq: | |||||
| nameServer: 202.165.179.86:9876 | |||||
| producer: | |||||
| retry-times-when-send-async-failed: 0 | |||||
| send-msg-timeout: 300000 | |||||
| compress-msg-body-over-howmuch: 4096 | |||||
| max-message-size: 4194304 | |||||
| retry-another-broker-when-not-store-ok: false | |||||
| retry-times-when-send-failed: 2 | |||||
| rabbitmq: | |||||
| host: 202.165.179.86 | |||||
| port: 5672 | |||||
| username: guest | |||||
| password: guest | |||||
| publisher-confirms: true | |||||
| virtual-host: / | |||||
| aop: | |||||
| proxy-target-class: true | |||||
| auto: true | |||||
| # @{link} https://github.com/abel533 | |||||
| #Mybatis | |||||
| mybatis: | |||||
| type-aliases-package: com.iformall.domain.po | |||||
| mapper-locations: classpath:mapper/*Mapper.xml | |||||
| configuration: | |||||
| map-underscore-to-camel-case: true | |||||
| cache-enabled: true | |||||
| lazy-loading-enabled: true | |||||
| use-generated-keys: true | |||||
| default-fetch-size: 100 | |||||
| default-statement-timeout: 10 | |||||
| #PageHelper | |||||
| pagehelper: | |||||
| helperDialect: mysql | |||||
| reasonable: false | |||||
| supportMethodsArguments: true | |||||
| params: count=countSql | |||||
| offset-as-page-num: true | |||||
| page-size-zero: true | |||||
| row-bounds-with-count: true | |||||
| mapper: | |||||
| mappers: | |||||
| - com.iformall.common.CommonMapper | |||||
| version: @project.version@ | |||||
| @@ -0,0 +1,100 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <configuration scan="true" scanPeriod="10 seconds"> | |||||
| <!-- 外部指定路径 --> | |||||
| <springProperty scop="context" name="logPath" source="logging.path" /> | |||||
| <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | |||||
| <encoder> | |||||
| <Pattern>[%date{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] --%mdc{client}%msg%n</Pattern> | |||||
| </encoder> | |||||
| </appender> | |||||
| <appender name="TRACE_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |||||
| <file>${logPath}/trace.log</file> | |||||
| <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |||||
| <FileNamePattern>${logPath}/daily/trace.%d{yyyy-MM-dd}.log</FileNamePattern> | |||||
| <maxHistory>180</maxHistory> <!-- 保留180天 --> | |||||
| </rollingPolicy> | |||||
| <layout> | |||||
| <pattern>[%date{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%logger:%line]--%mdc{client} %msg%n</pattern> | |||||
| </layout> | |||||
| </appender> | |||||
| <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |||||
| <file>${logPath}/info.log</file> | |||||
| <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |||||
| <FileNamePattern>${logPath}/daily/info.%d{yyyy-MM-dd}.log</FileNamePattern> | |||||
| <maxHistory>180</maxHistory> <!-- 保留180天 --> | |||||
| </rollingPolicy> | |||||
| <layout> | |||||
| <pattern>[%date{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%logger:%line]--%mdc{client} %msg%n</pattern> | |||||
| </layout> | |||||
| <filter class="ch.qos.logback.classic.filter.LevelFilter"> | |||||
| <level>INFO</level> | |||||
| <onMatch>ACCEPT</onMatch> | |||||
| <onMismatch>DENY</onMismatch> | |||||
| </filter> | |||||
| </appender> | |||||
| <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |||||
| <file>${logPath}/debug.log</file> | |||||
| <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |||||
| <FileNamePattern>${logPath}/daily/debug.%d{yyyy-MM-dd}.log</FileNamePattern> | |||||
| <maxHistory>180</maxHistory> <!-- 保留180天 --> | |||||
| </rollingPolicy> | |||||
| <layout> | |||||
| <pattern>[%date{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%logger:%line]--%mdc{client} %msg%n</pattern> | |||||
| </layout> | |||||
| <filter class="ch.qos.logback.classic.filter.LevelFilter"> | |||||
| <level>DEBUG</level> | |||||
| <onMatch>ACCEPT</onMatch> | |||||
| <onMismatch>DENY</onMismatch> | |||||
| </filter> | |||||
| </appender> | |||||
| <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |||||
| <file>${logPath}/warn.log</file> | |||||
| <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |||||
| <FileNamePattern>${logPath}/daily/warn.%d{yyyy-MM-dd}.log</FileNamePattern> | |||||
| <maxHistory>180</maxHistory> <!-- 保留180天 --> | |||||
| </rollingPolicy> | |||||
| <layout> | |||||
| <pattern>[%date{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%logger:%line]--%mdc{client} %msg%n</pattern> | |||||
| </layout> | |||||
| <filter class="ch.qos.logback.classic.filter.LevelFilter"> | |||||
| <level>WARN</level> | |||||
| <onMatch>ACCEPT</onMatch> | |||||
| <onMismatch>DENY</onMismatch> | |||||
| </filter> | |||||
| </appender> | |||||
| <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |||||
| <file>${logPath}/error.log</file> | |||||
| <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |||||
| <FileNamePattern>${logPath}/daily/error.%d{yyyy-MM-dd}.log</FileNamePattern> | |||||
| <maxHistory>180</maxHistory> <!-- 保留180天 --> | |||||
| </rollingPolicy> | |||||
| <layout> | |||||
| <pattern>[%date{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%logger:%line]--%mdc{client} %msg%n</pattern> | |||||
| </layout> | |||||
| <filter class="ch.qos.logback.classic.filter.LevelFilter"> | |||||
| <level>ERROR</level> | |||||
| <onMatch>ACCEPT</onMatch> | |||||
| <onMismatch>DENY</onMismatch> | |||||
| </filter> | |||||
| </appender> | |||||
| <root level="TRACE"> | |||||
| <appender-ref ref="TRACE_FILE" /> | |||||
| <appender-ref ref="INFO_FILE" /> | |||||
| <!-- <appender-ref ref="DEBUG_FILE" /> --> | |||||
| <!-- <appender-ref ref="WARN_FILE" /> --> | |||||
| <appender-ref ref="ERROR_FILE" /> | |||||
| </root> | |||||
| <root level="INFO"> | |||||
| <appender-ref ref="STDOUT" /> | |||||
| </root> | |||||
| </configuration> | |||||
| @@ -13,6 +13,7 @@ | |||||
| <modules> | <modules> | ||||
| <module>mybatis-multi-tenancy</module> | <module>mybatis-multi-tenancy</module> | ||||
| <module>mallinkService</module> | <module>mallinkService</module> | ||||
| <module>mallinkCallback</module> | |||||
| <module>mallinkAdmin</module> | <module>mallinkAdmin</module> | ||||
| <module>mallinkCApi</module> | <module>mallinkCApi</module> | ||||
| <module>mallinkBApi</module> | <module>mallinkBApi</module> | ||||