From fd64c134b0b56f9cec4e308229918ea6e82ac688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LI-CCONG=5C=E6=9D=8E=E8=81=AA=E8=81=AA?= <1441652193@qq.com> Date: Wed, 28 Feb 2024 18:15:28 +0800 Subject: [PATCH] =?UTF-8?q?jwt=E7=99=BB=E5=BD=95=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E3=80=81=E5=BE=AE=E4=BF=A1=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E3=80=81=E4=BB=A3=E7=A0=81=E8=87=AA=E5=8A=A8=E7=94=9F?= =?UTF-8?q?=E6=88=90=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nxhs-common/pom.xml | 46 +++++ .../cc/yunxi/common/domain/PageQueryPlus.java | 45 +++++ .../yunxi/common/domain/SortingFieldDTO.java | 34 ++++ .../java/cc/yunxi/common/genCodeMain.java | 82 +++++++++ nxhs-service/pom.xml | 9 + .../java/cc/yunxi/config/AuthProperties.java | 13 ++ .../java/cc/yunxi/config/JwtProperties.java | 16 ++ .../java/cc/yunxi/config/SecurityConfig.java | 33 ++++ .../java/cc/yunxi/config/WebMvcConfig.java | 76 ++++++++ .../java/cc/yunxi/config/WxHsyProperties.java | 17 ++ .../cc/yunxi/controller/ClientController.java | 20 ++ .../cc/yunxi/controller/CommonController.java | 96 ++++++++++ .../yunxi/controller/RecyclerController.java | 32 ++++ .../java/cc/yunxi/domain/dto/RecyclerDTO.java | 50 +++++ .../java/cc/yunxi/domain/dto/UserDTO.java | 21 +++ .../cc/yunxi/domain/dto/form/WxLoginDTO.java | 11 ++ .../main/java/cc/yunxi/domain/po/Client.java | 115 ++++++++++++ .../java/cc/yunxi/domain/po/Recycler.java | 48 +++++ .../cc/yunxi/domain/query/RecyclerQuery.java | 22 +++ .../yunxi/interceptor/LoginInterceptor.java | 40 ++++ .../yunxi/interceptor/StatsInterceptor.java | 36 ++++ .../java/cc/yunxi/mapper/ClientMapper.java | 18 ++ .../java/cc/yunxi/mapper/RecyclerMapper.java | 14 ++ .../java/cc/yunxi/service/ClientService.java | 16 ++ .../cc/yunxi/service/IRecyclerService.java | 23 +++ .../yunxi/service/impl/ClientServiceImpl.java | 20 ++ .../service/impl/RecyclerServiceImpl.java | 46 +++++ .../src/main/java/cc/yunxi/utils/JwtTool.java | 100 ++++++++++ .../java/cc/yunxi}/utils/UserContext.java | 17 +- .../main/java/cc/yunxi/utils/WeChatUtil.java | 173 ++++++++++++++++++ .../src/main/resources/application.yml | 30 +-- .../src/main/resources/logback-spring.xml | 22 --- .../main/resources/mapper/ClientMapper.xml | 5 + .../main/resources/mapper/RecyclerMapper.xml | 5 + nxhs-service/src/main/resources/nxhs.jks | Bin 0 -> 2217 bytes .../java/cc/yunxi/NxhsApplicationTest.java | 18 ++ pom.xml | 72 ++++---- 37 files changed, 1359 insertions(+), 82 deletions(-) create mode 100644 nxhs-common/src/main/java/cc/yunxi/common/domain/PageQueryPlus.java create mode 100644 nxhs-common/src/main/java/cc/yunxi/common/domain/SortingFieldDTO.java create mode 100644 nxhs-common/src/main/java/cc/yunxi/common/genCodeMain.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/config/AuthProperties.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/config/JwtProperties.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/config/SecurityConfig.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/config/WebMvcConfig.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/config/WxHsyProperties.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/controller/ClientController.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/controller/CommonController.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/controller/RecyclerController.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/domain/dto/RecyclerDTO.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/domain/dto/UserDTO.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/domain/dto/form/WxLoginDTO.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/domain/po/Client.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/domain/po/Recycler.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/domain/query/RecyclerQuery.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/interceptor/LoginInterceptor.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/interceptor/StatsInterceptor.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/mapper/ClientMapper.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/mapper/RecyclerMapper.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/service/ClientService.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/service/IRecyclerService.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/service/impl/ClientServiceImpl.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/service/impl/RecyclerServiceImpl.java create mode 100644 nxhs-service/src/main/java/cc/yunxi/utils/JwtTool.java rename {nxhs-common/src/main/java/cc/yunxi/common => nxhs-service/src/main/java/cc/yunxi}/utils/UserContext.java (51%) create mode 100644 nxhs-service/src/main/java/cc/yunxi/utils/WeChatUtil.java create mode 100644 nxhs-service/src/main/resources/mapper/ClientMapper.xml create mode 100644 nxhs-service/src/main/resources/mapper/RecyclerMapper.xml create mode 100644 nxhs-service/src/main/resources/nxhs.jks diff --git a/nxhs-common/pom.xml b/nxhs-common/pom.xml index c177837..1b307d2 100644 --- a/nxhs-common/pom.xml +++ b/nxhs-common/pom.xml @@ -30,6 +30,52 @@ tomcat-embed-core provided + + + com.mysql + mysql-connector-j + runtime + + + + com.baomidou + mybatis-plus-boot-starter + + + + com.baomidou + mybatis-plus-generator + 3.5.1 + + + org.freemarker + freemarker + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + + + cn.hutool + hutool-all + + + + org.hibernate.validator + hibernate-validator + + + + com.github.xiaoymin + knife4j-openapi2-spring-boot-starter + \ No newline at end of file diff --git a/nxhs-common/src/main/java/cc/yunxi/common/domain/PageQueryPlus.java b/nxhs-common/src/main/java/cc/yunxi/common/domain/PageQueryPlus.java new file mode 100644 index 0000000..d1347df --- /dev/null +++ b/nxhs-common/src/main/java/cc/yunxi/common/domain/PageQueryPlus.java @@ -0,0 +1,45 @@ +package cc.yunxi.common.domain; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import javax.validation.constraints.Min; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + + +@Data +@ApiModel(description = "分页查询条件") +@Accessors(chain = true) +public class PageQueryPlus { + public static final Integer DEFAULT_PAGE_SIZE = 20; + public static final Integer DEFAULT_PAGE_NUM = 1; + @ApiModelProperty("页码") + @Min(value = 1, message = "页码不能小于1") + private Integer pageNo = DEFAULT_PAGE_NUM; + + @ApiModelProperty("每页个数") + @Min(value = 1, message = "每页查询数量不能小于1") + private Integer pageSize = DEFAULT_PAGE_SIZE; + + @ApiModelProperty("排序规则") + private Collection sortingFields; + +// public Page buildPage() { +// Page page = new Page<>(pageNo, pageSize); +// if (!CollectionUtil.isEmpty(sortingFields)) { +// page.addOrder(sortingFields.stream().map(sortingField -> SortingFieldDTO.ORDER_ASC.equals(sortingField.getOrder()) ? +// OrderItem.asc(sortingField.getField()) : OrderItem.desc(sortingField.getField())) +// .collect(Collectors.toList())); +// } +// } + +} diff --git a/nxhs-common/src/main/java/cc/yunxi/common/domain/SortingFieldDTO.java b/nxhs-common/src/main/java/cc/yunxi/common/domain/SortingFieldDTO.java new file mode 100644 index 0000000..83483f9 --- /dev/null +++ b/nxhs-common/src/main/java/cc/yunxi/common/domain/SortingFieldDTO.java @@ -0,0 +1,34 @@ +package cc.yunxi.common.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SortingFieldDTO implements Serializable { + + /** + * 顺序 - 升序 + */ + public static final String ORDER_ASC = "asc"; + /** + * 顺序 - 降序 + */ + public static final String ORDER_DESC = "desc"; + + /** + * 字段 + */ + @ApiModelProperty("字段") + private String field; + /** + * 顺序 + */ + @ApiModelProperty("是否升序") + private String order; +} diff --git a/nxhs-common/src/main/java/cc/yunxi/common/genCodeMain.java b/nxhs-common/src/main/java/cc/yunxi/common/genCodeMain.java new file mode 100644 index 0000000..9694eb5 --- /dev/null +++ b/nxhs-common/src/main/java/cc/yunxi/common/genCodeMain.java @@ -0,0 +1,82 @@ +package cc.yunxi.common; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.generator.FastAutoGenerator; +import com.baomidou.mybatisplus.generator.config.OutputFile; +import com.baomidou.mybatisplus.generator.config.rules.DateType; +import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; +import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; +import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine; +import com.baomidou.mybatisplus.generator.fill.Column; + +import java.util.Collections; + +public class genCodeMain { + public static void main(String[] args) { + //1、配置数据源 + FastAutoGenerator.create("jdbc:mysql://222.71.165.188:3309/jnpf_ningxia", "root", "linus,.123") + //2、全局配置 + .globalConfig(builder -> { + builder.author("ccongli") // 设置作者名 + .outputDir(System.getProperty("user.dir") + "/src/main/java") //设置输出路径:项目的 java 目录下 + .commentDate("yyyy-MM-dd hh:mm:ss") //注释日期 + .dateType(DateType.ONLY_DATE) //定义生成的实体类中日期的类型 TIME_PACK=LocalDateTime;ONLY_DATE=Date; + .fileOverride() // 覆盖之前的文件 + .enableSwagger() // 开启 swagger 模式 + .disableOpenDir(); // 禁止打开输出目录,默认打开 + }) + //3、包配置 + .packageConfig(builder -> { + builder.parent("cc.yunxi") // 设置父包名 + .moduleName("") //设置模块包名 + .entity("domain.po") //pojo 实体类包名 + .service("service") //Service 包名 + .serviceImpl("service.impl") // ***ServiceImpl 包名 + .mapper("mapper") //Mapper 包名 + .xml("mapper") //Mapper XML 包名 + .controller("controller") //Controller 包名 + .other("utils") //自定义文件包名 + .pathInfo(Collections.singletonMap(OutputFile.mapperXml, System.getProperty("user.dir")+"/src/main/resources/mapper")); //配置 mapper.xml 路径信息:项目的 resources 目录下 + }) + //4、策略配置 + .strategyConfig(builder -> { + builder.addInclude("nx_client") // 设置需要生成的数据表名 + .addTablePrefix("nx") // 设置过滤表前缀 + //4.1、Mapper策略配置 + .mapperBuilder() + .superClass(BaseMapper.class) //设置父类 + .formatMapperFileName("%sMapper") //格式化 mapper 文件名称 + .enableMapperAnnotation() //开启 @Mapper 注解 + .formatXmlFileName("%sMapper") //格式化 Xml 文件名称 + + //4.2、service 策略配置 + .serviceBuilder() + .formatServiceFileName("%sService") //格式化 service 接口文件名称,%s进行匹配表名,如 UserService + .formatServiceImplFileName("%sServiceImpl") //格式化 service 实现类文件名称,%s进行匹配表名,如 UserServiceImpl + + //4.3、实体类策略配置 + .entityBuilder() + .enableLombok() //开启 Lombok + .disableSerialVersionUID() //不实现 Serializable 接口,不生产 SerialVersionUID +// .logicDeleteColumnName("deleted") //逻辑删除字段名 + .naming(NamingStrategy.underline_to_camel) // 数据库表映射到实体的命名策略:下划线转驼峰命 + .columnNaming(NamingStrategy.underline_to_camel) // 数据库表字段映射到实体的命名策略:下划线转驼峰命 + .addTableFills( + new Column("create_time", FieldFill.INSERT), + new Column("modify_time", FieldFill.INSERT_UPDATE) + ) //添加表字段填充,"create_time"字段自动填充为插入时间,"modify_time"字段自动填充为插入修改时间 + .enableTableFieldAnnotation() // 开启生成实体时生成字段注解 + + //4.4、Controller策略配置 + .controllerBuilder() + .formatFileName("%sController") //格式化 Controller 类文件名称,%s进行匹配表名,如 UserController + .enableRestStyle(); //开启生成 @RestController 控制器 + }) + //5、模板 + .templateEngine(new FreemarkerTemplateEngine()) +// .templateEngine(new VelocityTemplateEngine()) + //6、执行 + .execute(); + } +} diff --git a/nxhs-service/pom.xml b/nxhs-service/pom.xml index 959e9da..1eb6992 100644 --- a/nxhs-service/pom.xml +++ b/nxhs-service/pom.xml @@ -29,6 +29,15 @@ org.springframework.boot spring-boot-starter-web + + + org.springframework.security + spring-security-crypto + + + org.springframework.security + spring-security-rsa + \ No newline at end of file diff --git a/nxhs-service/src/main/java/cc/yunxi/config/AuthProperties.java b/nxhs-service/src/main/java/cc/yunxi/config/AuthProperties.java new file mode 100644 index 0000000..5fbf3f5 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/config/AuthProperties.java @@ -0,0 +1,13 @@ +package cc.yunxi.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.List; + +@Data +@ConfigurationProperties(prefix = "nxhs.auth") +public class AuthProperties { + private List includePaths; + private List excludePaths; +} diff --git a/nxhs-service/src/main/java/cc/yunxi/config/JwtProperties.java b/nxhs-service/src/main/java/cc/yunxi/config/JwtProperties.java new file mode 100644 index 0000000..838edab --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/config/JwtProperties.java @@ -0,0 +1,16 @@ +package cc.yunxi.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.core.io.Resource; + +import java.time.Duration; + +@Data +@ConfigurationProperties(prefix = "nxhs.jwt") +public class JwtProperties { + private Resource location; + private String password; + private String alias; + private Duration tokenTTL = Duration.ofMinutes(10); +} diff --git a/nxhs-service/src/main/java/cc/yunxi/config/SecurityConfig.java b/nxhs-service/src/main/java/cc/yunxi/config/SecurityConfig.java new file mode 100644 index 0000000..c664184 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/config/SecurityConfig.java @@ -0,0 +1,33 @@ +package cc.yunxi.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.rsa.crypto.KeyStoreKeyFactory; + +import java.security.KeyPair; + +@Configuration +@EnableConfigurationProperties(JwtProperties.class) +public class SecurityConfig { + + @Bean + public PasswordEncoder passwordEncoder(){ + return new BCryptPasswordEncoder(); + } + + @Bean + public KeyPair keyPair(JwtProperties properties){ + // 获取秘钥工厂 + KeyStoreKeyFactory keyStoreKeyFactory = + new KeyStoreKeyFactory( + properties.getLocation(), + properties.getPassword().toCharArray()); + //读取钥匙对 + return keyStoreKeyFactory.getKeyPair( + properties.getAlias(), + properties.getPassword().toCharArray()); + } +} \ No newline at end of file diff --git a/nxhs-service/src/main/java/cc/yunxi/config/WebMvcConfig.java b/nxhs-service/src/main/java/cc/yunxi/config/WebMvcConfig.java new file mode 100644 index 0000000..156688d --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/config/WebMvcConfig.java @@ -0,0 +1,76 @@ +package cc.yunxi.config; + +import cc.yunxi.interceptor.LoginInterceptor; +import cc.yunxi.interceptor.StatsInterceptor; +import cn.hutool.core.collection.CollUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.Arrays; +import java.util.List; + +@Configuration +@RequiredArgsConstructor +@EnableConfigurationProperties(AuthProperties.class) +public class WebMvcConfig implements WebMvcConfigurer { + private final LoginInterceptor loginInterceptor; + private final StatsInterceptor statsInterceptor; + private final AuthProperties authProperties; + + // 全局忽略路径 + private static List globalExcludePaths = Arrays.asList( + "/error", + "/favicon.ico", + "/v2/**", + "/v3/**", + "/swagger-resources/**", + "/webjars/**", + "/doc.html" + ); + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") // 所有接口 + .allowCredentials(true) // 是否发送 Cookie + .allowedOriginPatterns("*") // 支持域 + .allowedMethods("GET", "POST", "PUT", "DELETE") // 支持方法 + .allowedHeaders("*") + .exposedHeaders("*"); + } + + + @Override + public void addInterceptors(InterceptorRegistry registry) { + // 按顺序执行拦截器 + this.addStatsInterceptor(registry); + this.addLoginInterceptor(registry); + } + + // 添加登录拦截器 + private void addLoginInterceptor(InterceptorRegistry registry) { + InterceptorRegistration registration = registry.addInterceptor(loginInterceptor); + // 2.配置拦截路径 + List includePaths = authProperties.getIncludePaths(); + if (CollUtil.isNotEmpty(includePaths)) { + registration.addPathPatterns(includePaths); + } + // 3.配置放行路径 + List excludePaths = authProperties.getExcludePaths(); + if (CollUtil.isNotEmpty(excludePaths)) { + registration.excludePathPatterns(excludePaths); + } + registration.excludePathPatterns(globalExcludePaths); + } + + // 添加请求日志拦截器 + private void addStatsInterceptor(InterceptorRegistry registry) { + registry.addInterceptor(statsInterceptor) + .addPathPatterns("/**") + .excludePathPatterns(globalExcludePaths); + } +} diff --git a/nxhs-service/src/main/java/cc/yunxi/config/WxHsyProperties.java b/nxhs-service/src/main/java/cc/yunxi/config/WxHsyProperties.java new file mode 100644 index 0000000..3efbd1e --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/config/WxHsyProperties.java @@ -0,0 +1,17 @@ +package cc.yunxi.config; + + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 回收员微信配置 + */ +@Data +@Component +@ConfigurationProperties(prefix = "nxhs.wx.recycler") +public class WxHsyProperties { + private String appId; + private String appSecret; +} diff --git a/nxhs-service/src/main/java/cc/yunxi/controller/ClientController.java b/nxhs-service/src/main/java/cc/yunxi/controller/ClientController.java new file mode 100644 index 0000000..90d2c6e --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/controller/ClientController.java @@ -0,0 +1,20 @@ +package cc.yunxi.controller; + + +import org.springframework.web.bind.annotation.RequestMapping; + +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 散户信息 前端控制器 + *

+ * + * @author ccongli + * @since 2024-02-28 06:08:09 + */ +@RestController +@RequestMapping("/client") +public class ClientController { + +} diff --git a/nxhs-service/src/main/java/cc/yunxi/controller/CommonController.java b/nxhs-service/src/main/java/cc/yunxi/controller/CommonController.java new file mode 100644 index 0000000..22919fd --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/controller/CommonController.java @@ -0,0 +1,96 @@ +package cc.yunxi.controller; + +import cc.yunxi.common.domain.CommonResult; +import cc.yunxi.common.exception.BizIllegalException; +import cc.yunxi.config.WxHsyProperties; +import cc.yunxi.domain.dto.UserDTO; +import cc.yunxi.domain.dto.form.WxLoginDTO; +import cc.yunxi.domain.po.Recycler; +import cc.yunxi.service.IRecyclerService; +import cc.yunxi.utils.JwtTool; +import cc.yunxi.utils.WeChatUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +@Api(tags = "统一接口") +@RestController +@RequestMapping("/common") +@RequiredArgsConstructor +@Slf4j +public class CommonController { + + private final String code2SessionUrl = "https://api.weixin.qq.com/sns/jscode2session"; + + private final IRecyclerService recyclerService; + + @Resource + private WxHsyProperties wxHsyProperties; + + @Resource + private JwtTool jwtTool; + + @ApiOperation("登录") + @PostMapping("/login") + public CommonResult login(@RequestBody WxLoginDTO wxLoginDTO) { + log.info("login request body:{}", wxLoginDTO); + String url = code2SessionUrl +"?appid=" + wxHsyProperties.getAppId() + + "&secret="+ wxHsyProperties.getAppSecret() + + "&js_code=" + wxLoginDTO.getCode() + + "&grant_type=authorization_code"; + String jsonStr = HttpUtil.get(url, 30000); + log.info("WXserver code2Session return {}", jsonStr); + JSONObject jsonObject = JSONUtil.parseObj(jsonStr); + if (StrUtil.contains(jsonStr, "errcode")) { + throw new BizIllegalException("微信登录认证失败"); + } + String openId = jsonObject.getStr("openid"); + String sessionKey = jsonObject.getStr("session_key"); +// String unionId = jsonObject.getStr("unionid"); + // 解密得到用户手机号 + String userInfo = WeChatUtil.decryptData(wxLoginDTO.getEncryptedData(), sessionKey, wxLoginDTO.getIv()); + // 根据openId查找回收员 userType todo + Recycler recycler = recyclerService.getRecyclerByOpenid(openId); + if (recycler == null) { + // 回收员不存在,则注册 + if (StringUtils.isEmpty(userInfo)) { + throw new BizIllegalException("解密用户信息错误"); + } + JSONObject userObject = JSONUtil.parseObj(userInfo); + String phoneNumber = userObject.getStr("phoneNumber"); + recycler = recyclerService.registerRecycler(phoneNumber, openId); + } + UserDTO userDTO = new UserDTO(); + userDTO.setId(recycler.getId()); + userDTO.setUserType(1); // todo + userDTO.setUsername(recycler.getStaffsName()); + userDTO.setPhone(recycler.getMobilePhone()); + String token = jwtTool.createToken(userDTO); + userDTO.setToken(token); + return CommonResult.success(userDTO); + } + + + @ApiOperation("退出") + @GetMapping("/logout") + public CommonResult logout() { + return CommonResult.success("login success"); + } + + + @ApiOperation("用户查询接口") + @GetMapping("/search") + public CommonResult search() { + return CommonResult.success("users..."); + } +} diff --git a/nxhs-service/src/main/java/cc/yunxi/controller/RecyclerController.java b/nxhs-service/src/main/java/cc/yunxi/controller/RecyclerController.java new file mode 100644 index 0000000..bd56fd9 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/controller/RecyclerController.java @@ -0,0 +1,32 @@ +package cc.yunxi.controller; + +import cc.yunxi.common.domain.PageDTO; +import cc.yunxi.common.domain.PageQuery; +import cc.yunxi.domain.dto.RecyclerDTO; +import cc.yunxi.domain.po.Recycler; +import cc.yunxi.service.IRecyclerService; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = "回收员接口") +@RestController +@RequestMapping("/user") +@RequiredArgsConstructor +public class RecyclerController { + + private final IRecyclerService recyclerService; + + @ApiOperation("分页查询商品") + @GetMapping("/page") + public PageDTO queryItemByPage(PageQuery query) { + // 1.分页查询 + Page result = recyclerService.page(query.toMpPage("good_total", false)); + // 2.封装并返回 + return PageDTO.of(result, RecyclerDTO.class); + } +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/dto/RecyclerDTO.java b/nxhs-service/src/main/java/cc/yunxi/domain/dto/RecyclerDTO.java new file mode 100644 index 0000000..0a6328f --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/dto/RecyclerDTO.java @@ -0,0 +1,50 @@ +package cc.yunxi.domain.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@ApiModel(description = "回收员实体") +public class RecyclerDTO { + @ApiModelProperty("回收员id") + private Integer id; + + @ApiModelProperty("所属回收站id") + private String stationId; // 所属回收站 + + @ApiModelProperty("回收员姓名") + private String staffsName; // 回收员姓名 + + @ApiModelProperty("回收员电话") + private String mobilePhone; // 回收员电话 + + @ApiModelProperty("头像") + private String headIcon; // 头像 + + @ApiModelProperty("性别") + private Integer gender; // 性别 + + @ApiModelProperty("总里程数") + private Double recycleMiles; // 总里程数 + + @ApiModelProperty("总订单数") + private Integer orderTotal; // 总订单数 + + @ApiModelProperty("好评数") + private Integer goodTotal; // 好评数 + + @ApiModelProperty("总回收金额") + private BigDecimal orderAmount; // 总回收金额 + + @ApiModelProperty("自动接单(0-否,1-是)") + private Integer autoEnabled; // 自动接单(0-否,1-是) + + @ApiModelProperty("状态(0-禁用,1-启用)") + private Integer status; // 状态(0-禁用,1-启用) + + @ApiModelProperty("openid") + private String openid; // wx openid +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/dto/UserDTO.java b/nxhs-service/src/main/java/cc/yunxi/domain/dto/UserDTO.java new file mode 100644 index 0000000..edd0b40 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/dto/UserDTO.java @@ -0,0 +1,21 @@ +package cc.yunxi.domain.dto; + +import lombok.Data; + +/** + * 用户信息 DTO + */ +@Data +public class UserDTO { + + private Integer userType; // 1 回收员 2 散户 + + private String id; + + private String username; + + private String phone; + + private String token; // 访问token + +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/dto/form/WxLoginDTO.java b/nxhs-service/src/main/java/cc/yunxi/domain/dto/form/WxLoginDTO.java new file mode 100644 index 0000000..4be1064 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/dto/form/WxLoginDTO.java @@ -0,0 +1,11 @@ +package cc.yunxi.domain.dto.form; + +import lombok.Data; + +@Data +public class WxLoginDTO { + private String code; + private String encryptedData; + private String iv; + private Integer userType; // 1 回收员 2 散户 +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/po/Client.java b/nxhs-service/src/main/java/cc/yunxi/domain/po/Client.java new file mode 100644 index 0000000..5e674e7 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/po/Client.java @@ -0,0 +1,115 @@ +package cc.yunxi.domain.po; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.math.BigDecimal; +import java.util.Date; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +/** + *

+ * 散户信息 + *

+ * + * @author ccongli + * @since 2024-02-28 06:08:09 + */ +@Getter +@Setter +@TableName("nx_client") +@ApiModel(value = "Client对象", description = "散户信息") +public class Client { + + @ApiModelProperty("自然主键") + @TableId("id") + private String id; + + @ApiModelProperty("微信openid") + @TableField("wx_openid") + private String wxOpenid; + + @ApiModelProperty("微信名") + @TableField("wx_user_name") + private String wxUserName; + + @ApiModelProperty("昵称") + @TableField("nick_name") + private String nickName; + + @ApiModelProperty("手机") + @TableField("mobile_phone") + private String mobilePhone; + + @ApiModelProperty("头像") + @TableField("head_icon") + private String headIcon; + + @ApiModelProperty("性别") + @TableField("gender") + private Integer gender; + + @ApiModelProperty("生日") + @TableField("birthday") + private Date birthday; + + @ApiModelProperty("账户余额") + @TableField("banlance") + private BigDecimal banlance; + + @ApiModelProperty("会员码") + @TableField("membership_number") + private String membershipNumber; + + @ApiModelProperty("会员积分") + @TableField("membership_point") + private Integer membershipPoint; + + @ApiModelProperty("会员等级") + @TableField("membership_level") + private Integer membershipLevel; + + @ApiModelProperty("消费喜好") + @TableField("consume_preference") + private String consumePreference; + + @ApiModelProperty("发票抬头") + @TableField("tax_title") + private String taxTitle; + + @ApiModelProperty("企业税号") + @TableField("tax_id") + private String taxId; + + @ApiModelProperty("当前位置-经度") + @TableField("longitude") + private Double longitude; + + @ApiModelProperty("当前位置-纬度") + @TableField("latitude") + private Double latitude; + + @ApiModelProperty("最后登录时间") + @TableField("last_log_time") + private Date lastLogTime; + + @ApiModelProperty("最后登录IP") + @TableField("last_log_ip") + private String lastLogIp; + + @ApiModelProperty("有效标志(0-禁用,1-启用)") + @TableField("enabled_mark") + private Integer enabledMark; + + @ApiModelProperty("创建人") + @TableField("creator_user_id") + private String creatorUserId; + + @ApiModelProperty("创建时间") + @TableField("creator_time") + private Date creatorTime; + +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/po/Recycler.java b/nxhs-service/src/main/java/cc/yunxi/domain/po/Recycler.java new file mode 100644 index 0000000..7664ce6 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/po/Recycler.java @@ -0,0 +1,48 @@ +package cc.yunxi.domain.po; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +/** + * 回收员表 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("nx_recycle_station_staff") +public class Recycler { + + @TableId(value = "id", type = IdType.ASSIGN_UUID) + private String id; + + private String stationId; // 所属回收站 + + private String staffsName; // 回收员姓名 + + private String mobilePhone; // 回收员电话 + + private String headIcon; // 头像 + + private Integer gender; // 性别 + + private Double recycleMiles; // 总里程数 + + private Integer orderTotal; // 总订单数 + + private Integer goodTotal; // 好评数 + + private BigDecimal orderAmount; // 总回收金额 + + private Integer autoEnabled; // 自动接单(0-否,1-是) + + @TableField("enabled_mark") + private Integer status; // 状态(0-禁用,1-启用) + + private String openid; // wx openid +} diff --git a/nxhs-service/src/main/java/cc/yunxi/domain/query/RecyclerQuery.java b/nxhs-service/src/main/java/cc/yunxi/domain/query/RecyclerQuery.java new file mode 100644 index 0000000..6d61cab --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/domain/query/RecyclerQuery.java @@ -0,0 +1,22 @@ +package cc.yunxi.domain.query; + +import cc.yunxi.common.domain.PageQuery; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +@ApiModel(description = "回收员查询条件") +public class RecyclerQuery extends PageQuery { + + @ApiModelProperty("所属回收站") + private String stationId; + + @ApiModelProperty("联系电话") + private String mobilePhone; + + @ApiModelProperty("回收员姓名") + private String staffsName; +} diff --git a/nxhs-service/src/main/java/cc/yunxi/interceptor/LoginInterceptor.java b/nxhs-service/src/main/java/cc/yunxi/interceptor/LoginInterceptor.java new file mode 100644 index 0000000..df93d6e --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/interceptor/LoginInterceptor.java @@ -0,0 +1,40 @@ +package cc.yunxi.interceptor; + +import cc.yunxi.domain.dto.UserDTO; +import cc.yunxi.utils.JwtTool; +import cc.yunxi.utils.UserContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@RequiredArgsConstructor +@Slf4j +@Component +public class LoginInterceptor implements HandlerInterceptor { + + private final JwtTool jwtTool; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + // 1.获取请求头中的 token + String token = request.getHeader("authorization"); + // 2.校验token + UserDTO userInfo = jwtTool.parseToken(token); + log.info("当前用户信息: 【{}】", userInfo); + // 3.存入上下文 + UserContext.setUser(userInfo); + // 4.放行 + return true; + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + // 清理用户 + UserContext.removeUser(); + } +} diff --git a/nxhs-service/src/main/java/cc/yunxi/interceptor/StatsInterceptor.java b/nxhs-service/src/main/java/cc/yunxi/interceptor/StatsInterceptor.java new file mode 100644 index 0000000..0e860b7 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/interceptor/StatsInterceptor.java @@ -0,0 +1,36 @@ +package cc.yunxi.interceptor; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Component +@RequiredArgsConstructor +@Slf4j +public class StatsInterceptor implements HandlerInterceptor { + + public static final String METHOD_REQUEST_ELAPSED_TIME = "method_request_elapsed_time"; + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + request.setAttribute(METHOD_REQUEST_ELAPSED_TIME, System.currentTimeMillis()); + String requestURI = request.getRequestURI(); + String method = request.getMethod(); + String contentType = request.getContentType(); + log.info("请求路径:【{}】, 请求方法:【{}】, 协议:【{}】", requestURI, method, contentType); + return true; + } + + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + long startMillis = (long) request.getAttribute(METHOD_REQUEST_ELAPSED_TIME); + long endMillis = System.currentTimeMillis(); + log.info("请求耗时:【{}】ms", endMillis - startMillis); + } +} diff --git a/nxhs-service/src/main/java/cc/yunxi/mapper/ClientMapper.java b/nxhs-service/src/main/java/cc/yunxi/mapper/ClientMapper.java new file mode 100644 index 0000000..9e9e026 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/mapper/ClientMapper.java @@ -0,0 +1,18 @@ +package cc.yunxi.mapper; + +import cc.yunxi.domain.po.Client; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 散户信息 Mapper 接口 + *

+ * + * @author ccongli + * @since 2024-02-28 06:08:09 + */ +@Mapper +public interface ClientMapper extends BaseMapper { + +} diff --git a/nxhs-service/src/main/java/cc/yunxi/mapper/RecyclerMapper.java b/nxhs-service/src/main/java/cc/yunxi/mapper/RecyclerMapper.java new file mode 100644 index 0000000..e8438c9 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/mapper/RecyclerMapper.java @@ -0,0 +1,14 @@ +package cc.yunxi.mapper; + +import cc.yunxi.domain.po.Recycler; +import cc.yunxi.domain.po.Test; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 回收员表 mapper接口 + */ +@Mapper +public interface RecyclerMapper extends BaseMapper { + +} diff --git a/nxhs-service/src/main/java/cc/yunxi/service/ClientService.java b/nxhs-service/src/main/java/cc/yunxi/service/ClientService.java new file mode 100644 index 0000000..8c6c5d5 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/service/ClientService.java @@ -0,0 +1,16 @@ +package cc.yunxi.service; + +import cc.yunxi.domain.po.Client; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 散户信息 服务类 + *

+ * + * @author ccongli + * @since 2024-02-28 06:08:09 + */ +public interface ClientService extends IService { + +} diff --git a/nxhs-service/src/main/java/cc/yunxi/service/IRecyclerService.java b/nxhs-service/src/main/java/cc/yunxi/service/IRecyclerService.java new file mode 100644 index 0000000..4d2928f --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/service/IRecyclerService.java @@ -0,0 +1,23 @@ +package cc.yunxi.service; + +import cc.yunxi.domain.po.Recycler; +import cc.yunxi.domain.po.Test; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 测试表 业务接口 + */ +public interface IRecyclerService extends IService { + + /** + * 根据openid获取回收员信息 + * @param openid + * @return Recycler + */ + Recycler getRecyclerByOpenid(String openid); + + /** + * 手机号注册回收员 + */ + Recycler registerRecycler(String phoneNumber, String openId); +} diff --git a/nxhs-service/src/main/java/cc/yunxi/service/impl/ClientServiceImpl.java b/nxhs-service/src/main/java/cc/yunxi/service/impl/ClientServiceImpl.java new file mode 100644 index 0000000..98008c1 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/service/impl/ClientServiceImpl.java @@ -0,0 +1,20 @@ +package cc.yunxi.service.impl; + +import cc.yunxi.domain.po.Client; +import cc.yunxi.mapper.ClientMapper; +import cc.yunxi.service.ClientService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + *

+ * 散户信息 服务实现类 + *

+ * + * @author ccongli + * @since 2024-02-28 06:08:09 + */ +@Service +public class ClientServiceImpl extends ServiceImpl implements ClientService { + +} diff --git a/nxhs-service/src/main/java/cc/yunxi/service/impl/RecyclerServiceImpl.java b/nxhs-service/src/main/java/cc/yunxi/service/impl/RecyclerServiceImpl.java new file mode 100644 index 0000000..643b70e --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/service/impl/RecyclerServiceImpl.java @@ -0,0 +1,46 @@ +package cc.yunxi.service.impl; + +import cc.yunxi.common.exception.BizIllegalException; +import cc.yunxi.domain.po.Recycler; +import cc.yunxi.mapper.RecyclerMapper; +import cc.yunxi.service.IRecyclerService; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; + +/** + * 测试表 服务实现类 + */ +@Service +public class RecyclerServiceImpl extends ServiceImpl implements IRecyclerService { + + @Resource + RecyclerMapper recyclerMapper; + + @Override + public Recycler getRecyclerByOpenid(String openid) { + Recycler recycler = lambdaQuery().eq(Recycler::getOpenid, openid).one(); + return recycler; + } + + @Override + @Transactional + public Recycler registerRecycler(String phoneNumber, String openId) { + // 判断手机号是否被注册 + Recycler recycler = lambdaQuery().eq(Recycler::getMobilePhone, phoneNumber).one(); + if (ObjectUtil.isNotEmpty(recycler)) { + throw new BizIllegalException("该微信号已被注册"); + } + recycler = new Recycler(); + recycler.setMobilePhone(phoneNumber); + recycler.setStaffsName(phoneNumber); + recycler.setOpenid(openId); + recyclerMapper.insert(recycler); + return recycler; + } +} diff --git a/nxhs-service/src/main/java/cc/yunxi/utils/JwtTool.java b/nxhs-service/src/main/java/cc/yunxi/utils/JwtTool.java new file mode 100644 index 0000000..fceca06 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/utils/JwtTool.java @@ -0,0 +1,100 @@ +package cc.yunxi.utils; + +import cc.yunxi.common.exception.UnauthorizedException; +import cc.yunxi.config.JwtProperties; +import cc.yunxi.domain.dto.UserDTO; +import cn.hutool.core.exceptions.ValidateException; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.hutool.jwt.JWT; +import cn.hutool.jwt.JWTValidator; +import cn.hutool.jwt.signers.JWTSigner; +import cn.hutool.jwt.signers.JWTSignerUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.security.KeyPair; +import java.time.Duration; +import java.util.Date; + +@Component +public class JwtTool { + private final JWTSigner jwtSigner; + + @Autowired + @Lazy + private JwtProperties jwtProperties; + + public JwtTool(KeyPair keyPair) { + this.jwtSigner = JWTSignerUtil.createSigner("rs256", keyPair); + } + + /** + * 创建 access-token + * + * @param userDTO 用户信息 + * @return access-token + */ + public String createToken(UserDTO userDTO) { + // 1.生成jws + return JWT.create() + .setPayload("user", userDTO) + .setExpiresAt(new Date(System.currentTimeMillis() + jwtProperties.getTokenTTL().toMillis())) + .setSigner(jwtSigner) + .sign(); + } + + + /** + * 验证 token + */ + public JWT verifyToken(String token) { + // 1.校验token是否为空 + if (token == null) { + throw new UnauthorizedException("请先登录"); + } + // 2.校验jwt + JWT jwt; + try { + jwt = JWT.of(token).setSigner(jwtSigner); + } catch (Exception e) { + throw new UnauthorizedException("无效的token", e); + } + // 2.校验jwt是否有效 + if (!jwt.verify()) { + // 验证失败 + throw new UnauthorizedException("无效的token"); + } + // 3.校验是否过期 + try { + JWTValidator.of(jwt).validateDate(); + } catch (ValidateException e) { + throw new UnauthorizedException("token已过期"); + } + return jwt; + } + + /** + * 解析token + * + * @param token token + * @return 解析刷新token得到的用户信息 + */ + public UserDTO parseToken(String token) { + JWT jwt = verifyToken(token); + // 2.数据格式校验 + JSONObject userPayload = (JSONObject) jwt.getPayload("user"); + if (userPayload == null) { + // 数据为空 + throw new UnauthorizedException("无效的token"); + } + // 3.数据解析 + try { + return JSONUtil.toBean(userPayload, UserDTO.class); + } catch (RuntimeException e) { + // 数据格式有误 + throw new UnauthorizedException("无效的token", e); + } + } +} \ No newline at end of file diff --git a/nxhs-common/src/main/java/cc/yunxi/common/utils/UserContext.java b/nxhs-service/src/main/java/cc/yunxi/utils/UserContext.java similarity index 51% rename from nxhs-common/src/main/java/cc/yunxi/common/utils/UserContext.java rename to nxhs-service/src/main/java/cc/yunxi/utils/UserContext.java index bdd0ec7..55c6ac1 100644 --- a/nxhs-common/src/main/java/cc/yunxi/common/utils/UserContext.java +++ b/nxhs-service/src/main/java/cc/yunxi/utils/UserContext.java @@ -1,21 +1,26 @@ -package cc.yunxi.common.utils; +package cc.yunxi.utils; +import cc.yunxi.domain.dto.UserDTO; + +/** + * 用户信息上下文 + */ public class UserContext { - private static final ThreadLocal tl = new ThreadLocal<>(); + private static final ThreadLocal tl = new ThreadLocal<>(); /** * 保存当前登录用户信息到ThreadLocal - * @param userId 用户id + * @param userInfo 用户id */ - public static void setUser(Long userId) { - tl.set(userId); + public static void setUser(UserDTO userInfo) { + tl.set(userInfo); } /** * 获取当前登录用户信息 * @return 用户id */ - public static Long getUser() { + public static UserDTO getUser() { return tl.get(); } diff --git a/nxhs-service/src/main/java/cc/yunxi/utils/WeChatUtil.java b/nxhs-service/src/main/java/cc/yunxi/utils/WeChatUtil.java new file mode 100644 index 0000000..3965457 --- /dev/null +++ b/nxhs-service/src/main/java/cc/yunxi/utils/WeChatUtil.java @@ -0,0 +1,173 @@ +package cc.yunxi.utils; + +import cn.hutool.core.codec.Base64; +import cn.hutool.json.JSONObject; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.net.ssl.HttpsURLConnection; +import java.io.*; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.security.Security; +import java.util.Arrays; + +/** + * 微信小程序工具类 + * + * @author zhuhuix + * @date 2019-12-25 + */ +public class WeChatUtil { + private static final String KEY_ALGORITHM = "AES"; + private static final String ALGORITHM_STR = "AES/CBC/PKCS7Padding"; + private static Key key; + private static Cipher cipher; + + private static void init(byte[] keyBytes) { + // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要 + int base = 16; + if (keyBytes.length % base != 0) { + int groups = keyBytes.length / base + 1; + byte[] temp = new byte[groups * base]; + Arrays.fill(temp, (byte) 0); + System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length); + keyBytes = temp; + } + // 初始化 + Security.addProvider(new BouncyCastleProvider()); + // 转化成JAVA的密钥格式 + key = new SecretKeySpec(keyBytes, KEY_ALGORITHM); + try { + // 初始化cipher + cipher = Cipher.getInstance(ALGORITHM_STR, "BC"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static String decryptData(String encryptDataB64, String sessionKeyB64, String ivB64) { + return new String( + decryptOfDiyIv( + Base64.decode(encryptDataB64), + Base64.decode(sessionKeyB64), + Base64.decode(ivB64) + ) + ); + } + + /** + * 解密方法 + * + * @param encryptedData 要解密的字符串 + * @param keyBytes 解密密钥 + * @param ivs 自定义对称解密算法初始向量 iv + * @return 解密后的字节数组 + */ + private static byte[] decryptOfDiyIv(byte[] encryptedData, byte[] keyBytes, byte[] ivs) { + byte[] encryptedText = null; + init(keyBytes); + try { + cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivs)); + encryptedText = cipher.doFinal(encryptedData); + } catch (Exception e) { + e.printStackTrace(); + } + return encryptedText; + } + + public static String httpRequest(String requestUrl, String requestMethod, String output) { + try { + URL url = new URL(requestUrl); + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + connection.setDoOutput(true); + connection.setDoInput(true); + connection.setUseCaches(false); + connection.setRequestMethod(requestMethod); + if (null != output) { + OutputStream outputStream = connection.getOutputStream(); + outputStream.write(output.getBytes(StandardCharsets.UTF_8)); + outputStream.close(); + } + // 从输入流读取返回内容 + InputStream inputStream = connection.getInputStream(); + InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader); + String str; + StringBuilder buffer = new StringBuilder(); + while ((str = bufferedReader.readLine()) != null) { + buffer.append(str); + } + bufferedReader.close(); + inputStreamReader.close(); + inputStream.close(); + connection.disconnect(); + return buffer.toString(); + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param json 请求参数,请求参数应该是 json 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String httpPost(String url, JSONObject json) { + PrintWriter out = null; + BufferedReader in = null; + String result = ""; + try { + URL realUrl = new URL(url); + // 打开和URL之间的连接 + URLConnection conn = realUrl.openConnection(); + // 设置通用的请求属性 + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", + "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + // 发送POST请求必须设置如下两行 + conn.setDoOutput(true); + conn.setDoInput(true); + // 获取URLConnection对象对应的输出流 + out = new PrintWriter(conn.getOutputStream()); + // 发送请求参数 + out.print(json); + // flush输出流的缓冲 + out.flush(); + // 定义BufferedReader输入流来读取URL的响应 + in = new BufferedReader( + new InputStreamReader(conn.getInputStream())); + String line; + while ((line = in.readLine()) != null) { + result=result.concat(line); + } + } catch (Exception e) { + System.out.println("发送 POST 请求出现异常!" + e); + e.printStackTrace(); + } + //使用finally块来关闭输出流、输入流 + finally { + try { + if (out != null) { + out.close(); + } + if (in != null) { + in.close(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + return result; + } + +} \ No newline at end of file diff --git a/nxhs-service/src/main/resources/application.yml b/nxhs-service/src/main/resources/application.yml index 561895f..41dbd32 100644 --- a/nxhs-service/src/main/resources/application.yml +++ b/nxhs-service/src/main/resources/application.yml @@ -1,5 +1,7 @@ server: port: 8080 + servlet: + context-path: /api spring: application: @@ -56,16 +58,18 @@ knife4j: api-rule: package api-rule-resources: - cc.yunxi.controller -#hm: -# jwt: -# location: classpath:hmall.jks -# alias: hmall -# password: hmall123 -# tokenTTL: 30m -# auth: -# excludePaths: -# - /search/** -# - /users/login -# - /items/** -# - /hi -# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123 \ No newline at end of file +nxhs: + jwt: + location: classpath:nxhs.jks + alias: nxhs + password: nxhs2024 + tokenTTL: 1h + auth: + excludePaths: + - /common/login + - /test/** + wx: + recycler: # 回收员端 + appid: wxf82bcc798891a29d + appsecret: f37fb0ab2b5f691d8507acced60a58fb +# keytool -genkeypair -alias nxhs -keyalg RSA -keypass nxhs2024 -keystore nxhs.jks -storepass nxhs2024 \ No newline at end of file diff --git a/nxhs-service/src/main/resources/logback-spring.xml b/nxhs-service/src/main/resources/logback-spring.xml index 50ea71c..ce1c7a6 100644 --- a/nxhs-service/src/main/resources/logback-spring.xml +++ b/nxhs-service/src/main/resources/logback-spring.xml @@ -177,28 +177,6 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/nxhs-service/src/main/resources/mapper/ClientMapper.xml b/nxhs-service/src/main/resources/mapper/ClientMapper.xml new file mode 100644 index 0000000..807c4a4 --- /dev/null +++ b/nxhs-service/src/main/resources/mapper/ClientMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/nxhs-service/src/main/resources/mapper/RecyclerMapper.xml b/nxhs-service/src/main/resources/mapper/RecyclerMapper.xml new file mode 100644 index 0000000..807c4a4 --- /dev/null +++ b/nxhs-service/src/main/resources/mapper/RecyclerMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/nxhs-service/src/main/resources/nxhs.jks b/nxhs-service/src/main/resources/nxhs.jks new file mode 100644 index 0000000000000000000000000000000000000000..b2107ce68bc9de07c6e43e83f55b55e246a4f9d0 GIT binary patch literal 2217 zcmchY`8(7L7r^H`Gp0eN##+Q=>t-3g2qVUt>d~=?iJ$de+-E< z8-ZgB69AInM<5EpB7p$lfH)Kl089`#3BJ+^I3%pJ&oMI3ye(Hy=0p|9j;jUOrB@s! zIDBw!QjZ+6ScN*vq@Mk1nvB_q^3+$u2l+qBE*pLA!MAZP=AEDy>W^j>i5mKVwMS4g zbh`v%Vy=j@)oM#*O_XlG%$yjd=dMs^sj z!S-67QtP5#7q|UBU?+%~E|NJ?&y}JfU>P^byI#e9^Es+cd16!Kr1f0(Dv ze!geZz29{|&3#%hFZk|b=Sjpf5v&iMl0>QHZ*F>r4*yo_?60swy1CA;C^>5y9HY_g zV!h;j8UT z8_*uX<2pQbuJ6>Uu^l>jOkJa&U!za3RJ4YQ3iI@WQQ?(heAOT8avzJ3o+ds_Fk7fx ztKdf+V%U`n69|*})ti#5&Rd5mXLy%po@Td7sA_(>LEdY%*jHREQg4{fX*OtSFj&*t zu@~D?b$&@L_p4@J2yAy7dy}%bnr_VUC`po<)YYrwxpjBq4Iz=KSW9k=xvWI<^cmPn zcUJY11xaM{e5kMI9jTh|`>evmGhD{M51XQ8ecJ*kgv}%FC12Akj>!a@{!6EdJ?$i$ z@~~AAk}Ua2lHH}RimdxWyK!AA5Q@-aDb@F+!CD~W!2lO_5Z{SZLtGpZya=?DHhb`@ z1Ze=~-}*iK<#%Izg787h{wEvH)~?jrPsLnRG^P^yIfonTw0Ko`+NCFQGq7W9z>P86 z6cEPYPttrf!*~cWD0}XEWqUH>F=m6*AS8&c1(SwvRWD-{ zL+TBw2R;vmhcryNR)yKy2AHw+p)eC!Vn7?+LL2Vw>WVkL(3P+9s6ue7g!rD`Fk?$! z)rOBnI)172H&yf$U+&=EkHu+DTkE-)m+|KYr|E}$qG5$d3b*`{jCGg0OaijWg~psu zDYH_+K9?jY6;h)_%bjGxmnM7_Grw+$pIIGc)Qq#esWoisF8((scho}2_?i|mOENoq z+!9FJ9Uq*-k?a`x8u|7kUq1+FD9qfzk1sDW9f)ki*1U=5aMJlRu)5LRM=_=4tj=jN zafUE6a=6w?(+VNRc}Jv16`m-DfQz{ksEC+oG2C~nNCi28iJ_i|L8Z}y8kz6+!M?y*$cl3_*x8+aYE0gw{9|0TL?4X$6*_DgdO@4OOU@rq>K>7dS z{~-esAoO$D$4>!90!~3d62Jl_0RZF;)lGl7q?Ltzk0m($xnbz)t$+l~p!xNnT2#Y> zOH0MME$x{N*n-AkH9_wAgsYo*nZ)}Av6h(}vC?S{uyV2@Evu(9m-ND}KqTMDXNBg= z9bx&Z*SW_SaFWUh`pqtiRiwuI$>@v8S4m_ZhP|tF~ z7NM$sf%Tras4f8Oex~xM_7eu5cjE#2e#~{5kBj~@R;)N=luG~5 zw0d;aYGDTu61yFmSDQA6irc*~IZqH*u_)kdZ4<1#@Y* zZReA2U$rL#OLqSlkio}S-=SBgFJ!j(a-n=5w2^Ms;~xLK!I)O6k9+E?(-$?R1wU_u gZEj5LgTAk_+o=qUJ{UAjD-Q1({CJ{eGqTCxFR|9*PXGV_ literal 0 HcmV?d00001 diff --git a/nxhs-service/src/test/java/cc/yunxi/NxhsApplicationTest.java b/nxhs-service/src/test/java/cc/yunxi/NxhsApplicationTest.java index c706e6c..ccbefc1 100644 --- a/nxhs-service/src/test/java/cc/yunxi/NxhsApplicationTest.java +++ b/nxhs-service/src/test/java/cc/yunxi/NxhsApplicationTest.java @@ -1,10 +1,13 @@ package cc.yunxi; +import cc.yunxi.domain.dto.UserDTO; import cc.yunxi.service.ITestService; +import cc.yunxi.utils.JwtTool; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import javax.annotation.Resource; +import java.time.Duration; import java.util.List; @SpringBootTest @@ -13,9 +16,24 @@ public class NxhsApplicationTest { @Resource ITestService testService; + @Resource + JwtTool jwtTool; + @Test void test01() { List list = testService.list(); System.out.println(list); } + + + @Test + void test02() { + UserDTO userDTO = new UserDTO(); + userDTO.setId("10086"); + userDTO.setPhone("15137603460"); + userDTO.setUsername("小冲"); + String token = jwtTool.createToken(userDTO); + UserDTO userInfo = jwtTool.parseToken(token); + System.out.println(userInfo); + } } diff --git a/pom.xml b/pom.xml index ad93d1a..40edc15 100644 --- a/pom.xml +++ b/pom.xml @@ -23,9 +23,10 @@ 2.7.6 3.4.3 5.8.11 + 4.1.0 + 1.0.9.RELEASE - @@ -37,59 +38,49 @@ pom import + + cn.hutool + hutool-all + ${hutool.version} + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + com.github.xiaoymin + knife4j-openapi2-spring-boot-starter + ${knife4j-openapi2.version} + + + + org.springframework.security + spring-security-rsa + ${security-rsa.version} + - - - - com.mysql - mysql-connector-j - runtime - - - com.baomidou - mybatis-plus-boot-starter - ${mybatis-plus.version} - - + org.springframework.boot - spring-boot-starter-data-redis - - - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml + spring-boot-starter-test + test - + org.projectlombok lombok true - - cn.hutool - hutool-all - ${hutool.version} - - - - org.hibernate.validator - hibernate-validator - - - - com.github.xiaoymin - knife4j-openapi2-spring-boot-starter - 4.1.0 - - org.springframework.boot - spring-boot-starter-test - test + spring-boot-configuration-processor + true @@ -125,7 +116,6 @@ - alimaven