From d24dafc1fbe6918cda9aa9d6c855390c2c20f789 Mon Sep 17 00:00:00 2001 From: wangtaocs Date: Fri, 29 Nov 2024 11:09:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BE=AE=E4=BF=A1=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 9 +++ sql/mysql5/youlai_boot.sql | 1 + sql/mysql8/youlai_boot.sql | 1 + .../common/constant/SecurityConstants.java | 5 +- .../youlai/boot/config/SecurityConfig.java | 6 +- .../boot/config/WechatMiniAppConfig.java | 40 +++++++++++++ .../auth/controller/AuthController.java | 10 ++++ .../boot/shared/auth/service/AuthService.java | 7 +++ .../auth/service/impl/AuthServiceImpl.java | 57 +++++++++++++++++++ .../youlai/boot/system/model/entity/User.java | 5 ++ .../boot/system/model/form/UserForm.java | 3 + .../boot/system/service/UserService.java | 7 +++ .../system/service/impl/UserServiceImpl.java | 10 ++++ src/main/resources/application-dev.yml | 5 ++ 14 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/youlai/boot/config/WechatMiniAppConfig.java diff --git a/pom.xml b/pom.xml index 909da065..453c4a96 100644 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,9 @@ 4.6.4 2.2.1 + + + 4.5.5.B @@ -222,6 +225,12 @@ ${aliyun.java.sdk.dysmsapi.version} + + com.github.binarywang + weixin-java-miniapp + ${weixin-java.version} + + diff --git a/sql/mysql5/youlai_boot.sql b/sql/mysql5/youlai_boot.sql index 56a3c310..5b1370ab 100644 --- a/sql/mysql5/youlai_boot.sql +++ b/sql/mysql5/youlai_boot.sql @@ -405,6 +405,7 @@ CREATE TABLE `sys_user` ( `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', `update_by` bigint NULL DEFAULT NULL COMMENT '修改人ID', `is_deleted` tinyint(1) NULL DEFAULT 0 COMMENT '逻辑删除标识(0-未删除 1-已删除)', + `open_id` char(28) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信 openid', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `login_name`(`username` ASC) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户信息表' ROW_FORMAT = DYNAMIC; diff --git a/sql/mysql8/youlai_boot.sql b/sql/mysql8/youlai_boot.sql index 04328f58..ae78d1b7 100644 --- a/sql/mysql8/youlai_boot.sql +++ b/sql/mysql8/youlai_boot.sql @@ -367,6 +367,7 @@ `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', `update_by` bigint NULL DEFAULT NULL COMMENT '修改人ID', `is_deleted` tinyint(1) NULL DEFAULT 0 COMMENT '逻辑删除标识(0-未删除 1-已删除)', + `open_id` char(28) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信 openid', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `login_name`(`username` ASC) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户信息表' ROW_FORMAT = DYNAMIC; diff --git a/src/main/java/com/youlai/boot/common/constant/SecurityConstants.java b/src/main/java/com/youlai/boot/common/constant/SecurityConstants.java index 3a3128b0..f254a1c2 100644 --- a/src/main/java/com/youlai/boot/common/constant/SecurityConstants.java +++ b/src/main/java/com/youlai/boot/common/constant/SecurityConstants.java @@ -35,5 +35,8 @@ public interface SecurityConstants { */ String JWT_TOKEN_PREFIX = "Bearer "; - + /** + * 微信登录路径 + */ + String WX_LOGIN_PATH = "/api/v1/auth/wechatLogin"; } diff --git a/src/main/java/com/youlai/boot/config/SecurityConfig.java b/src/main/java/com/youlai/boot/config/SecurityConfig.java index 98f12c3f..1bd314c3 100644 --- a/src/main/java/com/youlai/boot/config/SecurityConfig.java +++ b/src/main/java/com/youlai/boot/config/SecurityConfig.java @@ -54,7 +54,11 @@ public class SecurityConfig { http .authorizeHttpRequests(requestMatcherRegistry -> - requestMatcherRegistry.requestMatchers(SecurityConstants.LOGIN_PATH).permitAll() + requestMatcherRegistry + .requestMatchers( + SecurityConstants.LOGIN_PATH, + SecurityConstants.WX_LOGIN_PATH) + .permitAll() .anyRequest().authenticated() ) .exceptionHandling(httpSecurityExceptionHandlingConfigurer -> diff --git a/src/main/java/com/youlai/boot/config/WechatMiniAppConfig.java b/src/main/java/com/youlai/boot/config/WechatMiniAppConfig.java new file mode 100644 index 00000000..6268f512 --- /dev/null +++ b/src/main/java/com/youlai/boot/config/WechatMiniAppConfig.java @@ -0,0 +1,40 @@ +package com.youlai.boot.config; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import cn.binarywang.wx.miniapp.config.WxMaConfig; +import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 配置微信 appId 和 appSecret + * @author wangtao + * @date 2024/11/26 17:28 + */ +@Setter +@ConfigurationProperties(prefix = "wechat.miniapp") +@Configuration +public class WechatMiniAppConfig { + + private String appId; + + private String appSecret; + + @Bean + public WxMaConfig wxMaConfig() { + WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl(); + config.setAppid(appId); + config.setSecret(appSecret); + return config; + } + + @Bean + public WxMaService wxMaService(WxMaConfig wxMaConfig) { + WxMaService service = new WxMaServiceImpl(); + service.setWxMaConfig(wxMaConfig); + return service; + } +} diff --git a/src/main/java/com/youlai/boot/shared/auth/controller/AuthController.java b/src/main/java/com/youlai/boot/shared/auth/controller/AuthController.java index 6defa625..8e649d3b 100644 --- a/src/main/java/com/youlai/boot/shared/auth/controller/AuthController.java +++ b/src/main/java/com/youlai/boot/shared/auth/controller/AuthController.java @@ -61,4 +61,14 @@ public class AuthController { AuthTokenResponse authTokenResponse = authService.refreshToken(request); return Result.success(authTokenResponse); } + + @Operation(summary = "微信登录") + @PostMapping("/wechatLogin") + @Log(value = "微信登录", module = LogModuleEnum.LOGIN) + public Result wechatLogin( + @Parameter(description = "微信授权码", example = "code") @RequestParam String code + ) { + AuthTokenResponse loginResult = authService.wechatLogin(code); + return Result.success(loginResult); + } } diff --git a/src/main/java/com/youlai/boot/shared/auth/service/AuthService.java b/src/main/java/com/youlai/boot/shared/auth/service/AuthService.java index ca462a54..80190226 100644 --- a/src/main/java/com/youlai/boot/shared/auth/service/AuthService.java +++ b/src/main/java/com/youlai/boot/shared/auth/service/AuthService.java @@ -40,4 +40,11 @@ public interface AuthService { * @return 登录结果 */ AuthTokenResponse refreshToken(RefreshTokenRequest request); + + /** + * 微信登录 + * @param code 微信登录code + * @return 登录结果 + */ + AuthTokenResponse wechatLogin(String code); } diff --git a/src/main/java/com/youlai/boot/shared/auth/service/impl/AuthServiceImpl.java b/src/main/java/com/youlai/boot/shared/auth/service/impl/AuthServiceImpl.java index 5ef814a9..b831dbb5 100644 --- a/src/main/java/com/youlai/boot/shared/auth/service/impl/AuthServiceImpl.java +++ b/src/main/java/com/youlai/boot/shared/auth/service/impl/AuthServiceImpl.java @@ -1,11 +1,14 @@ package com.youlai.boot.shared.auth.service.impl; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; import cn.hutool.captcha.AbstractCaptcha; import cn.hutool.captcha.CaptchaUtil; import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import com.youlai.boot.common.constant.SecurityConstants; +import com.youlai.boot.common.constant.SystemConstants; import com.youlai.boot.common.exception.BusinessException; import com.youlai.boot.common.result.ResultCode; import com.youlai.boot.core.security.util.SecurityUtils; @@ -16,8 +19,12 @@ import com.youlai.boot.shared.auth.model.CaptchaResponse; import com.youlai.boot.shared.auth.model.AuthTokenResponse; import com.youlai.boot.config.property.CaptchaProperties; import com.youlai.boot.shared.auth.service.TokenService; +import com.youlai.boot.system.model.entity.User; +import com.youlai.boot.system.model.form.UserForm; +import com.youlai.boot.system.service.UserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -26,6 +33,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import java.awt.*; +import java.util.Objects; import java.util.concurrent.TimeUnit; /** @@ -45,6 +53,8 @@ public class AuthServiceImpl implements AuthService { private final Font captchaFont; private final CaptchaProperties captchaProperties; private final TokenService tokenService; + private final WxMaService wxMaService; + private final UserService userService; /** * 登录 @@ -148,4 +158,51 @@ public class AuthServiceImpl implements AuthService { return tokenService.refreshToken(refreshToken); } + @Override + public AuthTokenResponse wechatLogin(String code) { + // 1. 通过code获取微信access_token + WxMaJscode2SessionResult sessionInfo = null; + try { + sessionInfo = wxMaService.getUserService().getSessionInfo(code); + } catch (WxErrorException e) { + log.error("微信小程序登录失败", e); + throw new BusinessException(e); + } + + String openId = sessionInfo.getOpenid(); + if (StrUtil.isBlank(openId)) { + throw new BusinessException("微信授权失败"); + } + + // todo 获取微信用户信息 +// WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(sessionInfo.getSessionKey(), sessionInfo.getOpenid()); + + // 2. 根据openId查询用户信息,如果不存在则注册新用户 + User user = userService.getUserByOpenId(openId); + + if (Objects.isNull(user)) { + String name = "微信用户" + IdUtil.simpleUUID(); + UserForm newUser = new UserForm(); + newUser.setOpenId(openId); + newUser.setNickname(name); + newUser.setUsername(name); + boolean result = userService.saveUser(newUser); + if (!result) { + + throw new BusinessException("微信用户注册失败"); + } + } + user = userService.getUserByOpenId(openId); + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(user.getUsername().toLowerCase().trim(), SystemConstants.DEFAULT_PASSWORD); + // 执行用户认证 + Authentication authentication = authenticationManager.authenticate(authenticationToken); + // 认证成功后生成JWT令牌 + AuthTokenResponse authTokenResponse = tokenService.generateToken(authentication); + // 将认证信息存入Security上下文,便于在AOP(如日志记录)中获取当前用户信息 + SecurityContextHolder.getContext().setAuthentication(authentication); + // 返回包含JWT令牌的登录结果 + return authTokenResponse; + } + } diff --git a/src/main/java/com/youlai/boot/system/model/entity/User.java b/src/main/java/com/youlai/boot/system/model/entity/User.java index 62535380..71e74f7f 100644 --- a/src/main/java/com/youlai/boot/system/model/entity/User.java +++ b/src/main/java/com/youlai/boot/system/model/entity/User.java @@ -72,4 +72,9 @@ public class User extends BaseEntity { * 是否删除(0-否 1-是) */ private Integer isDeleted; + + /** + * 微信openid + */ + private String openId; } \ No newline at end of file diff --git a/src/main/java/com/youlai/boot/system/model/form/UserForm.java b/src/main/java/com/youlai/boot/system/model/form/UserForm.java index 8b48259a..8d4f8423 100644 --- a/src/main/java/com/youlai/boot/system/model/form/UserForm.java +++ b/src/main/java/com/youlai/boot/system/model/form/UserForm.java @@ -56,4 +56,7 @@ public class UserForm { @NotEmpty(message = "用户角色不能为空") private List roleIds; + @Schema(description="微信openId") + private String openId; + } diff --git a/src/main/java/com/youlai/boot/system/service/UserService.java b/src/main/java/com/youlai/boot/system/service/UserService.java index 9c083488..b2ced182 100644 --- a/src/main/java/com/youlai/boot/system/service/UserService.java +++ b/src/main/java/com/youlai/boot/system/service/UserService.java @@ -158,4 +158,11 @@ public interface UserService extends IService { * @return {@link List>} 用户选项列表 */ List> listUserOptions(); + + /** + * 根据openId获取用户信息 + * @param openId openId + * @return {@link User} + */ + User getUserByOpenId(String openId); } diff --git a/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java index 36da5402..ec323638 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java @@ -445,4 +445,14 @@ public class UserServiceImpl extends ServiceImpl implements Us } return Collections.emptyList(); } + + /** + * 根据openId获取用户信息 + * @param openId openId + * @return {@link User} + */ + @Override + public User getUserByOpenId(String openId) { + return this.getOne(new LambdaQueryWrapper().eq(User::getOpenId, openId)); + } } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index c4f6ea39..967c8102 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -214,3 +214,8 @@ captcha: # 验证码有效期(秒) expire-seconds: 120 +# 微信小程序 +wechat: + miniapp: + appId: xxxxxx + appSecret: xxxxxx \ No newline at end of file