This commit is contained in:
haoxr
2024-12-01 21:31:09 +08:00
17 changed files with 175 additions and 7 deletions

View File

@@ -54,6 +54,9 @@
<!-- 阿里云短信 -->
<aliyun.java.sdk.core.version>4.6.4</aliyun.java.sdk.core.version>
<aliyun.java.sdk.dysmsapi.version>2.2.1</aliyun.java.sdk.dysmsapi.version>
<!-- 微信 jdk -->
<weixin-java.version>4.5.5.B</weixin-java.version>
</properties>
<dependencies>
@@ -223,6 +226,12 @@
<version>${aliyun.java.sdk.dysmsapi.version}</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>${weixin-java.version}</version>
</dependency>
</dependencies>
<build>

View File

@@ -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) 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;

View File

@@ -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) 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;

View File

@@ -35,5 +35,8 @@ public interface SecurityConstants {
*/
String JWT_TOKEN_PREFIX = "Bearer ";
/**
* 微信登录路径
*/
String WECHAT_LOGIN_PATH = "/api/v1/auth/wechat-login";
}

View File

@@ -54,7 +54,11 @@ public class SecurityConfig {
http
.authorizeHttpRequests(requestMatcherRegistry ->
requestMatcherRegistry.requestMatchers(SecurityConstants.LOGIN_PATH).permitAll()
requestMatcherRegistry
.requestMatchers(
SecurityConstants.LOGIN_PATH,
SecurityConstants.WECHAT_LOGIN_PATH)
.permitAll()
.anyRequest().authenticated()
)
.exceptionHandling(httpSecurityExceptionHandlingConfigurer ->

View File

@@ -0,0 +1,41 @@
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
* @since 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;
}
}

View File

@@ -61,4 +61,14 @@ public class AuthController {
AuthTokenResponse authTokenResponse = authService.refreshToken(request);
return Result.success(authTokenResponse);
}
@Operation(summary = "微信登录")
@PostMapping("/wechat-login")
@Log(value = "微信登录", module = LogModuleEnum.LOGIN)
public Result<AuthTokenResponse> wechatLogin(
@Parameter(description = "微信授权码", example = "code") @RequestParam String code
) {
AuthTokenResponse loginResult = authService.wechatLogin(code);
return Result.success(loginResult);
}
}

View File

@@ -40,4 +40,12 @@ public interface AuthService {
* @return 登录结果
*/
AuthTokenResponse refreshToken(RefreshTokenRequest request);
/**
* 微信小程序登录
*
* @param code 微信登录code
* @return 登录结果
*/
AuthTokenResponse wechatLogin(String code);
}

View File

@@ -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,54 @@ public class AuthServiceImpl implements AuthService {
return tokenService.refreshToken(refreshToken);
}
/**
* 微信小程序登录
*
* @param code 微信登录code
* @return 访问令牌
*/
@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("微信用户注册失败");
}
}
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;
}
}

View File

@@ -57,7 +57,7 @@ public class ConfigController {
}
@Operation(summary = "刷新系统配置缓存")
@PutMapping(value = "/refresh")
@PutMapping("/refresh")
@PreAuthorize("@ss.hasPerm('sys:config:refresh')")
public Result<ConfigForm> refreshCache() {
return Result.judge(configService.refreshCache());

View File

@@ -83,8 +83,7 @@ public class NoticeController {
}
@Operation(summary = "发布通知公告")
@PatchMapping(value = "/{id}/publish")
@PutMapping(value = "/{id}/publish")
@PutMapping("/{id}/publish")
@PreAuthorize("@ss.hasPerm('sys:notice:publish')")
public Result<Void> publishNotice(
@Parameter(description = "通知公告ID") @PathVariable Long id
@@ -94,8 +93,7 @@ public class NoticeController {
}
@Operation(summary = "撤回通知公告")
@PutMapping(value = "/{id}/revoke")
@PatchMapping(value = "/{id}/revoke")
@PutMapping("/{id}/revoke")
@PreAuthorize("@ss.hasPerm('sys:notice:revoke')")
public Result<Void> revokeNotice(
@Parameter(description = "通知公告ID") @PathVariable Long id

View File

@@ -72,4 +72,9 @@ public class User extends BaseEntity {
* 是否删除(0-否 1-是)
*/
private Integer isDeleted;
/**
* 微信openid
*/
private String openId;
}

View File

@@ -56,4 +56,7 @@ public class UserForm {
@NotEmpty(message = "用户角色不能为空")
private List<Long> roleIds;
@Schema(description="微信openId")
private String openId;
}

View File

@@ -158,4 +158,12 @@ public interface UserService extends IService<User> {
* @return {@link List<Option<String>>} 用户选项列表
*/
List<Option<String>> listUserOptions();
/**
* 根据openId获取用户信息
*
* @param openId openId
* @return {@link User}
*/
User getUserByOpenId(String openId);
}

View File

@@ -83,6 +83,7 @@ public class ConfigServiceImpl extends ServiceImpl<ConfigMapper, Config> impleme
"配置键已存在");
Config config = configConverter.toEntity(configForm);
config.setCreateBy(SecurityUtils.getUserId());
config.setIsDeleted(0);
return this.save(config);
}

View File

@@ -457,4 +457,15 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
}
return Collections.emptyList();
}
/**
* 根据openId获取用户信息
*
* @param openId openId
* @return {@link User}
*/
@Override
public User getUserByOpenId(String openId) {
return this.getOne(new LambdaQueryWrapper<User>().eq(User::getOpenId, openId));
}
}

View File

@@ -214,3 +214,8 @@ captcha:
# 验证码有效期(秒)
expire-seconds: 120
# 微信小程序
wechat:
miniapp:
appId: xxxxxx
appSecret: xxxxxx