小程序登录相关

This commit is contained in:
张松
2025-02-11 17:33:08 +08:00
parent 4c383e70cf
commit 15db6821df
28 changed files with 541 additions and 11 deletions

View File

@@ -17,7 +17,7 @@ import java.util.Map;
* @author Administrator
*/
@RestController
@RequestMapping("auth")
@RequestMapping("/admin/auth")
public class AuthorizationController {
@Resource
private AuthorizationService authorizationService;

View File

@@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RestController;
* @author zs
*/
@RestController
@RequestMapping("/menus")
@RequestMapping("/admin/menus")
public class MenuController {
@Resource

View File

@@ -19,7 +19,7 @@ import java.time.LocalDateTime;
* @author Administrator
*/
@RestController
@RequestMapping("/merchantRegister")
@RequestMapping("/admin/merchantRegister")
public class MerchantRegisterController {
@Resource
private MerchantRegisterService merchantRegisterService;

View File

@@ -19,7 +19,7 @@ import org.springframework.web.bind.annotation.*;
* @author Administrator
*/
@RestController
@RequestMapping("/role")
@RequestMapping("/admin/role")
public class RoleController {
@Resource

View File

@@ -17,7 +17,7 @@ import org.springframework.web.bind.annotation.*;
* @author Administrator
*/
@RestController
@RequestMapping("shopInfo")
@RequestMapping("/admin/shopInfo")
public class ShopInfoController {
private final ShopInfoService shopInfoService;

View File

@@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.*;
* @author Administrator
*/
@RestController
@RequestMapping("/shopMerchant")
@RequestMapping("/admin/shopMerchant")
public class ShopMerchantController {
@Resource
private ShopMerchantService shopMerchantService;

View File

@@ -0,0 +1,29 @@
package com.czg.controller;
import com.czg.account.dto.auth.LoginTokenDTO;
import com.czg.account.dto.auth.UserAuthorizationLoginDTO;
import com.czg.account.service.UserAuthorizationService;
import com.czg.resp.CzgResult;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
/**
* 用户登录相关
* @author Administrator
*/
@RestController
@RequestMapping("/user")
public class UserAuthorizationController {
@Resource
private UserAuthorizationService userAuthorizationService;
/**
* 小程序登录
* @param userAuthorizationLoginDTO 登录信息
* @return 登录信息
*/
@PostMapping("/login")
public CzgResult<LoginTokenDTO> login(UserAuthorizationLoginDTO userAuthorizationLoginDTO) {
return CzgResult.success(userAuthorizationService.login(userAuthorizationLoginDTO));
}
}

View File

@@ -33,3 +33,4 @@ dubbo:
threads: 20
threadpool: fixed

View File

@@ -1,7 +1,7 @@
server:
port: 9100
servlet:
context-path: /admin
# servlet:
# context-path: /admin
spring:
application:
@@ -11,3 +11,15 @@ spring:
logging:
config: classpath:logback.xml
wx:
appId: wxd88fffa983758a30
secrete: a34a61adc0602118b49400baa8812454
alipay:
serverUrl: https://openapi.alipay.com/gateway.do
appId: 2021004145625815
privateKey: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCAjDBuS8K/IJb9ui+KuNm/sTUdEiaji4BNpZ92avO1N5JpNlGmac6ec4p3tNFT950sBLcQkClcUpQxUHQzAT6DYNNXOKyvfI/EmcqwCw6PaMNLs/8cV//J2WWZBUhLaOsjKurpm9/3W5MnTh4BGxIfBoeBMA8f8K3BgKdmyKtvIEV2h2cyjsMskdn+g6oNZcmWcms0pvpPHyH46mRaGFhpp0v19wX3WsamGldh1L2VntmaDN3C2XbSrXv90XYp5bEUqwTbLwXpMAlzTibF56d/iqv9oYi8cpAKougUFLOymnbutLNs2tLrEDSFwHcmG2/wbZHybZyYcIgFgv4arf+tAgMBAAECggEAf7hKKlw1y6Z6vvAtalxNZUuRZSfyog3p1bwYWxTavZPQcZ7Zs0lvVDmiO1u5m/7q96BbryY9IhCeUv0H5uF2lhwu/3s9AEL3qTPQkeb6eXxyhhX6A9RfPdM1Qbtg4CQHdHKg4qjP9znSVHwmDZ0y/QaEvdPdQzPjv92u9c2tn4N4x6XyBYcU5gzxiJNnIugCmBgcJo/3H2fgV+XXEhORPvy5of9b4oATHEaLS/8dAS2wuOjhzaGS4MXp3VkXn3XaYjwSzaL03qYWA+xm+aO5sJv8bmqZW7sNVck5o3sPo7cQ4VkBFVzyrRdmJcxcSRJ9MsB9JsrhoKI8pgaXrVie4QKBgQDU2vai0lpBIK/0jzRpPNoqdT8lnafnnWni8nU4kfAh+gCLi+HBPhQRT0kv4unQc2q2/gALE7sgZVO00JGY5a3R0orsojPoUSZlpypGW7GGqKy576NHn0nw4o/PdfysT92VWgt1hlfTf6qfCDhfE9APU+RGvlSWXcT8nxVel3iUaQKBgQCamoJN6+4v+chJvL2nqV8NVVRLp0vDIHxs1QOtKwUodx8Qp1D6CJYtavCXn8aNUFVNQJPJ7TQPpJjXP2rI4SN01weDwx+I+wh8PBGHV6/234R+6TvFgY1PrYgCdfNP4i/E7B4uyEhAxdU73PB8qkqRAeJGok05p7oG71KCOBiYpQKBgEZfGflcuDAeAW5GRhqg3rP4zWa/R7qgZVh9tll8jjp9b96y4XFE99d9MgId8BVVgyt6sEL5Q/2C4ni+F9TH4n6jMADp42VkJuCmsqhOOlP9whU67+2G8Sgtj0QUivPg964f9ffl8XVgGOW5DwIIB9p5btggptCLscufQK5kP545AoGADBvf6tR4wl8w9b2HqTMV08iEIqzGvVC1Dh0c/Zop/EJgN4CzUfIMOSBwGaAVAApzs+pD6QPgGP2OTwWTioo/qa4R05sbxDHNN1XJFa2jhZV6HiqMWOrNs5jm1zJ/zRjtHuJTdtyO9CvKiLbESy9XScY4/8lEfSiK5HIoJzTXkFUCgYAkYkvkW6psJpWj05XWq44UN0n6QOU/Igl35Um/iuOMVhsTmIt09STQVTuzJzfH82+sCqoRsD1blE5unKNUC1DK77aNKTv3Z0dxN9R7FAyfZRiYQXTrbBPBqWjay6FCNxn8e8UsJN4Z6FIV2LGlQI114krSap1MALKLVvnld0NaUQ==
alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiQkrz+emAuS1mB3KKDOMmAZRd/BlPbh7fAIHAqAj1+QCZNcV3o2BTLIIqnuKpSlFXDG3uDzp2VsBxcizXuBbFyPGylnD9CgCj5abyh3+FIHPAZ2IM3TtpqImZ0TSPGXrMli4Nir7MvZktgccCqQKCC4o6iaDGz+UwWwJUIPna8fm2tiTZ+KH150CZbKVj4ZGNpBh5XSV/1dRgyQIV9D/EwSbkZ0n6VgKQLJBi0C2UE3QB17aL1Ir6+gDXIDbknN8O7GUD3aMGdThYdSRUb5wp9CZ5qfV7vCS/CgaRo38nhH3NOzkTL+7v0m1ZDHPmqEkn9VzZN6sCQdL7PoAOjHOCwIDAQAB
encryptKey: Hp1TbhOqevbHCA5ji/VlqQ==

View File

@@ -54,7 +54,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
// 重置根路径防止satoken切割根路径导致匹配不到路径
ApplicationInfo.routePrefix = "";
SaRouter.match("/user/**")
SaRouter.match("/user/**").notMatch("/user/login")
.check(r -> StpKit.USER.checkLogin())
.setHit(true)
// .match("/**")

View File

@@ -0,0 +1,15 @@
package com.czg.account.dto.auth;
import com.czg.account.entity.UserInfo;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @author Administrator
*/
@AllArgsConstructor
@Data
public class LoginTokenDTO {
private String token;
private UserInfo userInfo;
}

View File

@@ -0,0 +1,25 @@
package com.czg.account.dto.auth;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
/**
* @author Administrator
*/
@Data
public class UserAuthorizationLoginDTO {
/**
* 登录类型
*/
@NotEmpty(message = "登录类型")
private String source;
/**
* 登录code
*/
@NotEmpty(message = "登录code")
private String code;
/**
* 登录返回的data
*/
private String rawData;
}

View File

@@ -0,0 +1,17 @@
package com.czg.account.dto.auth;
import lombok.Data;
/**
* @author Administrator
*/
@Data
public class WechatRawDataDTO {
private String nickName;
private Integer gender;
private String language;
private String city;
private String province;
private String country;
private String avatarUrl;
}

View File

@@ -0,0 +1,99 @@
package com.czg.account.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 实体类。
*
* @author Administrator
* @since 2025-02-11
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("tb_user_info")
public class UserInfo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* id
*/
@Id(keyType = KeyType.Auto)
private Long id;
/**
* 用户头像
*/
private String headImg;
/**
* 用户昵称
*/
private String nickName;
/**
* 电话号码
*/
private String phone;
/**
* 会员生日
*/
private String birthDay;
/**
* 0-女 1男
*/
private Integer sex;
/**
* 微信 openid
*/
private String wechatOpenId;
/**
* 支付宝 openid
*/
private String alipayOpenId;
/**
* 1正常
*/
private Integer status;
/**
* 最近登录时间
*/
private LocalDateTime lastLoginTime;
/**
* 登录密码
*/
private String password;
/**
* 支付密码
*/
private String payPwd;
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
@Column(onInsertValue = "now()", onUpdateValue = "now()")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,12 @@
package com.czg.account.service;
import com.czg.account.dto.auth.LoginTokenDTO;
import com.czg.account.dto.auth.UserAuthorizationLoginDTO;
/**
* @author Administrator
*/
public interface UserAuthorizationService {
LoginTokenDTO login(UserAuthorizationLoginDTO userAuthorizationLoginDTO);
}

View File

@@ -0,0 +1,14 @@
package com.czg.account.service;
import com.czg.account.entity.UserInfo;
import com.mybatisflex.core.service.IService;
/**
* 服务层。
*
* @author Administrator
* @since 2025-02-11
*/
public interface UserInfoService extends IService<UserInfo> {
}

View File

@@ -13,6 +13,10 @@
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>

View File

@@ -0,0 +1,22 @@
package com.czg.enums;
import lombok.Getter;
/**
* 登录类型枚举
* @author Administrator
*/
@Getter
public enum UserAuthSourceEnum {
WECHAT("微信", "wechat"),
ALIPAY("支付宝", "alipay");
private final String name;
private final String value;
UserAuthSourceEnum(String name, String value) {
this.name = name;
this.value = value;
}
}

View File

@@ -0,0 +1,102 @@
package com.czg.utils;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipayEncrypt;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.czg.exception.CzgException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* 支付宝通用SDK工具类
*
* @author tankaikai
* @since 2024-09-23 16:15
*/
@Slf4j
@Component
public class AlipayUtil {
/**
* 网关地址 线上https://openapi.alipay.com/gateway.do 沙箱https://openapi.alipaydev.com/gateway.do
*/
@Value("${alipay.serverUrl}")
private String serverUrl;
/**
* 应用ID
*/
@Value("${alipay.appId}")
private String appId;
/**
* 应用私钥
*/
@Value("${alipay.privateKey}")
private String privateKey;
/**
* 支付宝公钥
*/
@Value("${alipay.alipayPublicKey}")
private String alipayPublicKey;
/**
* 支付宝公钥
*/
@Value("${alipay.encryptKey}")
private String encryptKey;
/**
* 创建支付宝客户端
* @return AlipayClient
*/
@SneakyThrows
public AlipayClient createClient() {
AlipayConfig alipayConfig = new AlipayConfig();
//设置网关地址
alipayConfig.setServerUrl(serverUrl);
//设置应用ID
alipayConfig.setAppId(appId);
//设置应用私钥
alipayConfig.setPrivateKey(privateKey);
//设置支付宝公钥
alipayConfig.setAlipayPublicKey(alipayPublicKey);
return new DefaultAlipayClient(alipayConfig);
}
/**
* 获取支付宝用户的openId
* @param code 用户信息授权码
* @return openId
*/
public String getOpenId(String code){
AlipaySystemOauthTokenRequest req = new AlipaySystemOauthTokenRequest();
//SDK已经封装掉了公共参数这里只需要传入业务参数
req.setCode(code);
req.setGrantType("authorization_code");
//此次只是参数展示,未进行字符串转义,实际情况下请转义
//req.setBizContent(" {" + " \"primary_industry_name\":\"IT科技/IT软件与服务\"," + " \"primary_industry_code\":\"10001/20102\"," + " \"secondary_industry_code\":\"10001/20102\"," + " \"secondary_industry_name\":\"IT科技/IT软件与服务\"" + " }");
AlipaySystemOauthTokenResponse response;
try {
response = createClient().execute(req);
}catch (Exception e){
log.error("获取支付宝用户信息失败", e);
throw new RuntimeException(e);
}
log.info("获取支付宝用户信息成功,返回结果:{}", response.getBody());
//调用失败
if (!response.isSuccess()) {
log.error("获取支付宝用户信息失败,错误码:{},错误信息:{}", response.getSubCode(), response.getSubMsg());
throw new RuntimeException(StrUtil.format("获取支付宝用户信息失败,错误码:{},错误信息:{}", response.getSubCode(), response.getSubMsg()));
}
//调用成功则处理业务逻辑为配合支付系统确定沿用支付宝的老标准使用userId
return response.getUserId();
}
}

View File

@@ -0,0 +1,67 @@
package com.czg.utils;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author Administrator
*/
@Slf4j
@Component
public class WechatAuthUtil {
@Value("${wx.appId}")
private String appId;
@Value("${wx.secrete}")
private String secrete;
static LinkedHashMap<String,String> linkedHashMap=new LinkedHashMap<>();
static {
linkedHashMap.put("40001","获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口");
linkedHashMap.put("40003","不合法的 OpenID ,请开发者确认 OpenID (该用户)是否已关注公众号,或是否是其他公众号的 OpenID");
linkedHashMap.put("40014","不合法的 access_token ,请开发者认真比对 access_token 的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口");
linkedHashMap.put("40037","不合法的 template_id");
linkedHashMap.put("43101","用户未订阅消息");
linkedHashMap.put("43107","订阅消息能力封禁");
linkedHashMap.put("43108","并发下发消息给同一个粉丝");
linkedHashMap.put("45168","命中敏感词");
linkedHashMap.put("47003","参数错误");
}
public String getSessionKeyOrOpenId(String code) {
String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";
Map<String, Object> requestUrlParam = new HashMap<>();
// https://mp.weixin.qq.com/wxopen/devprofile?action=get_profile&token=164113089&lang=zh_CN
//小程序appId
requestUrlParam.put("appid", appId);
//小程序secret
requestUrlParam.put("secret", secrete);
//小程序端返回的code
requestUrlParam.put("js_code", code);
//默认参数
requestUrlParam.put("grant_type", "authorization_code");
//发送post请求读取调用微信接口获取openid用户唯一标识
String resp = HttpUtil.post(requestUrl, requestUrlParam);
JSONObject jsonObject = JSON.parseObject(resp);
log.info("微信获取openid响应报文{}", resp);
String openid = jsonObject.getString("openid");
if (StrUtil.isBlank(openid)) {
throw new RuntimeException("openId获取失败");
}
return openid;
}
}

View File

@@ -43,6 +43,11 @@
<artifactId>cash-common-tools</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.39.99.ALL</version>
</dependency>
<!-- Sa-Token 权限认证在线文档https://sa-token.cc -->
<!-- sa-token 的漏洞的依赖处理 -->
<!-- sa-token start -->

View File

@@ -27,7 +27,10 @@
<groupId>com.czg</groupId>
<artifactId>cash-common-tools</artifactId>
</dependency>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
</dependency>
</dependencies>

View File

@@ -0,0 +1,14 @@
package com.czg.service.account.mapper;
import com.czg.account.entity.UserInfo;
import com.mybatisflex.core.BaseMapper;
/**
* 映射层。
*
* @author Administrator
* @since 2025-02-11
*/
public interface UserInfoMapper extends BaseMapper<UserInfo> {
}

View File

@@ -0,0 +1,63 @@
package com.czg.service.account.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.czg.account.dto.auth.LoginTokenDTO;
import com.czg.account.dto.auth.UserAuthorizationLoginDTO;
import com.czg.account.dto.auth.WechatRawDataDTO;
import com.czg.account.entity.UserInfo;
import com.czg.account.service.UserAuthorizationService;
import com.czg.enums.UserAuthSourceEnum;
import com.czg.sa.StpKit;
import com.czg.account.service.UserInfoService;
import com.czg.utils.AlipayUtil;
import com.czg.utils.WechatAuthUtil;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @author Administrator
*/
@Service
public class UserAuthorizationServiceImpl implements UserAuthorizationService {
@Resource
private WechatAuthUtil wechatAuthUtil;
@Resource
private AlipayUtil alipayUtil;
@Resource
private UserInfoService userInfoService;
@Override
public LoginTokenDTO login(UserAuthorizationLoginDTO userAuthorizationLoginDTO) {
UserInfo userInfo;
String openId;
if (UserAuthSourceEnum.WECHAT.getValue().equals(userAuthorizationLoginDTO.getSource())) {
openId = wechatAuthUtil.getSessionKeyOrOpenId(userAuthorizationLoginDTO.getCode());
userInfo = userInfoService.queryChain().eq(UserInfo::getWechatOpenId, openId).one();
}else {
openId = alipayUtil.getOpenId(userAuthorizationLoginDTO.getCode());
userInfo = userInfoService.queryChain().eq(UserInfo::getAlipayOpenId, openId).one();
}
if (ObjectUtil.isNull(userInfo)) {
userInfo = new UserInfo();
if (StrUtil.isNotBlank(userAuthorizationLoginDTO.getRawData())) {
WechatRawDataDTO wechatRawDataDTO = JSONObject.parseObject(userAuthorizationLoginDTO.getRawData(), WechatRawDataDTO.class);
userInfo.setHeadImg(wechatRawDataDTO.getAvatarUrl());
userInfo.setNickName(StrUtil.isNotBlank(wechatRawDataDTO.getNickName()) ? wechatRawDataDTO.getNickName() : "微信用户");
}
userInfo.setWechatOpenId(openId);
userInfo.setStatus(1);
userInfo.setLastLoginTime(DateUtil.date().toLocalDateTime());
} else {
userInfo.setLastLoginTime(DateUtil.date().toLocalDateTime());
}
userInfoService.saveOrUpdate(userInfo);
StpKit.USER.login(userInfo.getId());
return new LoginTokenDTO(StpKit.USER.getTokenValue(), userInfo);
}
}

View File

@@ -0,0 +1,18 @@
package com.czg.service.account.service.impl;
import com.czg.account.entity.UserInfo;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.service.account.mapper.UserInfoMapper;
import com.czg.account.service.UserInfoService;
import org.springframework.stereotype.Service;
/**
* 服务层实现。
*
* @author Administrator
* @since 2025-02-11
*/
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService{
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.account.mapper.UserInfoMapper">
</mapper>

View File

@@ -69,7 +69,7 @@ public class Main {
//设置表前缀和只生成哪些表setGenerateTable 未配置时,生成所有表
globalConfig.getStrategyConfig()
.setTablePrefix("tb")
.setGenerateTable("tb_shop_merchant");
.setGenerateTable("tb_user_info");
//设置生成 entity 并启用 Lombok
globalConfig.enableEntity()

View File

@@ -83,6 +83,7 @@
<version>2.10.0</version>
<scope>test</scope>
</dependency>
<!-- end -->
<!--
<dependency>