| New file |
| | |
| | | package com.sunsail; |
| | | |
| | | import org.springframework.boot.SpringApplication; |
| | | import org.springframework.boot.autoconfigure.SpringBootApplication; |
| | | import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; |
| | | |
| | | /** |
| | | * 启动程序 |
| | | * |
| | | * @author Lion Li |
| | | */ |
| | | |
| | | @SpringBootApplication |
| | | public class SunSailApplication { |
| | | |
| | | public static void main(String[] args) { |
| | | SpringApplication application = new SpringApplication(SunSailApplication.class); |
| | | application.setApplicationStartup(new BufferingApplicationStartup(2048)); |
| | | application.run(args); |
| | | System.out.println("(♥◠‿◠)ノ゙ RuoYi-Vue-Plus启动成功 ლ(´ڡ`ლ)゙"); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail; |
| | | |
| | | import org.springframework.boot.builder.SpringApplicationBuilder; |
| | | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; |
| | | |
| | | /** |
| | | * web容器中进行部署 |
| | | * |
| | | * @author Lion Li |
| | | */ |
| | | public class SunSailServletInitializer extends SpringBootServletInitializer { |
| | | |
| | | @Override |
| | | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { |
| | | return application.sources(SunSailApplication.class); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.controller; |
| | | |
| | | import cn.dev33.satoken.annotation.SaIgnore; |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import com.sunsail.common.core.utils.MapstructUtils; |
| | | import com.sunsail.common.core.utils.MessageUtils; |
| | | import com.sunsail.common.core.utils.StreamUtils; |
| | | import com.sunsail.common.core.utils.StringUtils; |
| | | import com.sunsail.common.core.utils.ValidatorUtils; |
| | | import jakarta.servlet.http.HttpServletRequest; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import me.zhyd.oauth.model.AuthResponse; |
| | | import me.zhyd.oauth.model.AuthUser; |
| | | import me.zhyd.oauth.request.AuthRequest; |
| | | import me.zhyd.oauth.utils.AuthStateUtils; |
| | | import com.sunsail.common.core.constant.UserConstants; |
| | | import com.sunsail.common.core.domain.R; |
| | | import com.sunsail.common.core.domain.model.LoginBody; |
| | | import com.sunsail.common.core.domain.model.RegisterBody; |
| | | import com.sunsail.common.core.domain.model.SocialLoginBody; |
| | | import com.sunsail.common.encrypt.annotation.ApiEncrypt; |
| | | import com.sunsail.common.json.utils.JsonUtils; |
| | | import com.sunsail.common.satoken.utils.LoginHelper; |
| | | import com.sunsail.common.social.config.properties.SocialLoginConfigProperties; |
| | | import com.sunsail.common.social.config.properties.SocialProperties; |
| | | import com.sunsail.common.social.utils.SocialUtils; |
| | | import com.sunsail.common.tenant.helper.TenantHelper; |
| | | import com.sunsail.common.websocket.utils.WebSocketUtils; |
| | | import com.sunsail.system.domain.SysClient; |
| | | import com.sunsail.system.domain.bo.SysTenantBo; |
| | | import com.sunsail.system.domain.vo.SysTenantVo; |
| | | import com.sunsail.system.service.ISysClientService; |
| | | import com.sunsail.system.service.ISysConfigService; |
| | | import com.sunsail.system.service.ISysSocialService; |
| | | import com.sunsail.system.service.ISysTenantService; |
| | | import com.sunsail.web.domain.vo.LoginTenantVo; |
| | | import com.sunsail.web.domain.vo.LoginVo; |
| | | import com.sunsail.web.domain.vo.TenantListVo; |
| | | import com.sunsail.web.service.IAuthStrategy; |
| | | import com.sunsail.web.service.SysLoginService; |
| | | import com.sunsail.web.service.SysRegisterService; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.net.URL; |
| | | import java.util.List; |
| | | import java.util.concurrent.ScheduledExecutorService; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * 认证 |
| | | * |
| | | * @author Lion Li |
| | | */ |
| | | @Slf4j |
| | | @SaIgnore |
| | | @RequiredArgsConstructor |
| | | @RestController |
| | | @RequestMapping("/auth") |
| | | public class AuthController { |
| | | |
| | | private final SocialProperties socialProperties; |
| | | private final SysLoginService loginService; |
| | | private final SysRegisterService registerService; |
| | | private final ISysConfigService configService; |
| | | private final ISysTenantService tenantService; |
| | | private final ISysSocialService socialUserService; |
| | | private final ISysClientService clientService; |
| | | private final ScheduledExecutorService scheduledExecutorService; |
| | | |
| | | |
| | | /** |
| | | * 登录方法 |
| | | * |
| | | * @param body 登录信息 |
| | | * @return 结果 |
| | | */ |
| | | @ApiEncrypt |
| | | @PostMapping("/login") |
| | | public R<LoginVo> login(@RequestBody String body) { |
| | | LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class); |
| | | ValidatorUtils.validate(loginBody); |
| | | // 授权类型和客户端id |
| | | String clientId = loginBody.getClientId(); |
| | | String grantType = loginBody.getGrantType(); |
| | | SysClient client = clientService.queryByClientId(clientId); |
| | | // 查询不到 client 或 client 内不包含 grantType |
| | | if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) { |
| | | log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType); |
| | | return R.fail(MessageUtils.message("auth.grant.type.error")); |
| | | } else if (!UserConstants.NORMAL.equals(client.getStatus())) { |
| | | return R.fail(MessageUtils.message("auth.grant.type.blocked")); |
| | | } |
| | | // 校验租户 |
| | | loginService.checkTenant(loginBody.getTenantId()); |
| | | // 登录 |
| | | LoginVo loginVo = IAuthStrategy.login(body, client, grantType); |
| | | |
| | | Long userId = LoginHelper.getUserId(); |
| | | scheduledExecutorService.schedule(() -> { |
| | | WebSocketUtils.sendMessage(userId, "欢迎登录RuoYi-Vue-Plus后台管理系统"); |
| | | }, 3, TimeUnit.SECONDS); |
| | | return R.ok(loginVo); |
| | | } |
| | | |
| | | /** |
| | | * 第三方登录请求 |
| | | * |
| | | * @param source 登录来源 |
| | | * @return 结果 |
| | | */ |
| | | @GetMapping("/binding/{source}") |
| | | public R<String> authBinding(@PathVariable("source") String source) { |
| | | SocialLoginConfigProperties obj = socialProperties.getType().get(source); |
| | | if (ObjectUtil.isNull(obj)) { |
| | | return R.fail(source + "平台账号暂不支持"); |
| | | } |
| | | AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties); |
| | | String authorizeUrl = authRequest.authorize(AuthStateUtils.createState()); |
| | | return R.ok("操作成功", authorizeUrl); |
| | | } |
| | | |
| | | /** |
| | | * 第三方登录回调业务处理 绑定授权 |
| | | * |
| | | * @param loginBody 请求体 |
| | | * @return 结果 |
| | | */ |
| | | @PostMapping("/social/callback") |
| | | public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) { |
| | | // 获取第三方登录信息 |
| | | AuthResponse<AuthUser> response = SocialUtils.loginAuth( |
| | | loginBody.getSource(), loginBody.getSocialCode(), |
| | | loginBody.getSocialState(), socialProperties); |
| | | AuthUser authUserData = response.getData(); |
| | | // 判断授权响应是否成功 |
| | | if (!response.ok()) { |
| | | return R.fail(response.getMsg()); |
| | | } |
| | | loginService.socialRegister(authUserData); |
| | | return R.ok(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 取消授权 |
| | | * |
| | | * @param socialId socialId |
| | | */ |
| | | @DeleteMapping(value = "/unlock/{socialId}") |
| | | public R<Void> unlockSocial(@PathVariable Long socialId) { |
| | | Boolean rows = socialUserService.deleteWithValidById(socialId); |
| | | return rows ? R.ok() : R.fail("取消授权失败"); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 退出登录 |
| | | */ |
| | | @PostMapping("/logout") |
| | | public R<Void> logout() { |
| | | loginService.logout(); |
| | | return R.ok("退出成功"); |
| | | } |
| | | |
| | | /** |
| | | * 用户注册 |
| | | */ |
| | | @ApiEncrypt |
| | | @PostMapping("/register") |
| | | public R<Void> register(@Validated @RequestBody RegisterBody user) { |
| | | if (!configService.selectRegisterEnabled(user.getTenantId())) { |
| | | return R.fail("当前系统没有开启注册功能!"); |
| | | } |
| | | registerService.register(user); |
| | | return R.ok(); |
| | | } |
| | | |
| | | /** |
| | | * 登录页面租户下拉框 |
| | | * |
| | | * @return 租户列表 |
| | | */ |
| | | @GetMapping("/tenant/list") |
| | | public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception { |
| | | List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo()); |
| | | List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class); |
| | | // 获取域名 |
| | | String host; |
| | | String referer = request.getHeader("referer"); |
| | | if (StringUtils.isNotBlank(referer)) { |
| | | // 这里从referer中取值是为了本地使用hosts添加虚拟域名,方便本地环境调试 |
| | | host = referer.split("//")[1].split("/")[0]; |
| | | } else { |
| | | host = new URL(request.getRequestURL().toString()).getHost(); |
| | | } |
| | | // 根据域名进行筛选 |
| | | List<TenantListVo> list = StreamUtils.filter(voList, vo -> |
| | | StringUtils.equals(vo.getDomain(), host)); |
| | | // 返回对象 |
| | | LoginTenantVo vo = new LoginTenantVo(); |
| | | vo.setVoList(CollUtil.isNotEmpty(list) ? list : voList); |
| | | vo.setTenantEnabled(TenantHelper.isEnable()); |
| | | return R.ok(vo); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.controller; |
| | | |
| | | import cn.dev33.satoken.annotation.SaIgnore; |
| | | import cn.hutool.captcha.AbstractCaptcha; |
| | | import cn.hutool.captcha.generator.CodeGenerator; |
| | | import cn.hutool.core.util.IdUtil; |
| | | import cn.hutool.core.util.RandomUtil; |
| | | import com.sunsail.common.core.constant.Constants; |
| | | import com.sunsail.common.core.constant.GlobalConstants; |
| | | import com.sunsail.common.core.domain.R; |
| | | import com.sunsail.common.core.utils.SpringUtils; |
| | | import com.sunsail.common.core.utils.StringUtils; |
| | | import com.sunsail.common.core.utils.reflect.ReflectUtils; |
| | | import com.sunsail.common.mail.config.properties.MailProperties; |
| | | import com.sunsail.common.mail.utils.MailUtils; |
| | | import com.sunsail.common.ratelimiter.annotation.RateLimiter; |
| | | import com.sunsail.common.ratelimiter.enums.LimitType; |
| | | import com.sunsail.common.redis.utils.RedisUtils; |
| | | import com.sunsail.common.web.config.properties.CaptchaProperties; |
| | | import com.sunsail.common.web.enums.CaptchaType; |
| | | import org.dromara.sms4j.api.SmsBlend; |
| | | import org.dromara.sms4j.api.entity.SmsResponse; |
| | | import org.dromara.sms4j.core.factory.SmsFactory; |
| | | import org.dromara.sms4j.provider.enumerate.SupplierType; |
| | | import com.sunsail.web.domain.vo.CaptchaVo; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.expression.Expression; |
| | | import org.springframework.expression.ExpressionParser; |
| | | import org.springframework.expression.spel.standard.SpelExpressionParser; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.time.Duration; |
| | | import java.util.LinkedHashMap; |
| | | |
| | | /** |
| | | * 验证码操作处理 |
| | | * |
| | | * @author Lion Li |
| | | */ |
| | | @SaIgnore |
| | | @Slf4j |
| | | @Validated |
| | | @RequiredArgsConstructor |
| | | @RestController |
| | | public class CaptchaController { |
| | | |
| | | private final CaptchaProperties captchaProperties; |
| | | private final MailProperties mailProperties; |
| | | |
| | | /** |
| | | * 短信验证码 |
| | | * |
| | | * @param phonenumber 用户手机号 |
| | | */ |
| | | @RateLimiter(key = "#phonenumber", time = 60, count = 1) |
| | | @GetMapping("/resource/sms/code") |
| | | public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) { |
| | | String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber; |
| | | String code = RandomUtil.randomNumbers(4); |
| | | RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); |
| | | // 验证码模板id 自行处理 (查数据库或写死均可) |
| | | String templateId = ""; |
| | | LinkedHashMap<String, String> map = new LinkedHashMap<>(1); |
| | | map.put("code", code); |
| | | SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA); |
| | | SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map); |
| | | if (!"OK".equals(smsResponse.getCode())) { |
| | | log.error("验证码短信发送异常 => {}", smsResponse); |
| | | return R.fail(smsResponse.getMessage()); |
| | | } |
| | | return R.ok(); |
| | | } |
| | | |
| | | /** |
| | | * 邮箱验证码 |
| | | * |
| | | * @param email 邮箱 |
| | | */ |
| | | @RateLimiter(key = "#email", time = 60, count = 1) |
| | | @GetMapping("/resource/email/code") |
| | | public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) { |
| | | if (!mailProperties.getEnabled()) { |
| | | return R.fail("当前系统没有开启邮箱功能!"); |
| | | } |
| | | String key = GlobalConstants.CAPTCHA_CODE_KEY + email; |
| | | String code = RandomUtil.randomNumbers(4); |
| | | RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); |
| | | try { |
| | | MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。"); |
| | | } catch (Exception e) { |
| | | log.error("验证码短信发送异常 => {}", e.getMessage()); |
| | | return R.fail(e.getMessage()); |
| | | } |
| | | return R.ok(); |
| | | } |
| | | |
| | | /** |
| | | * 生成验证码 |
| | | */ |
| | | @RateLimiter(time = 60, count = 10, limitType = LimitType.IP) |
| | | @GetMapping("/auth/code") |
| | | public R<CaptchaVo> getCode() { |
| | | CaptchaVo captchaVo = new CaptchaVo(); |
| | | boolean captchaEnabled = captchaProperties.getEnable(); |
| | | if (!captchaEnabled) { |
| | | captchaVo.setCaptchaEnabled(false); |
| | | return R.ok(captchaVo); |
| | | } |
| | | // 保存验证码信息 |
| | | String uuid = IdUtil.simpleUUID(); |
| | | String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid; |
| | | // 生成验证码 |
| | | CaptchaType captchaType = captchaProperties.getType(); |
| | | boolean isMath = CaptchaType.MATH == captchaType; |
| | | Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength(); |
| | | CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length); |
| | | AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz()); |
| | | captcha.setGenerator(codeGenerator); |
| | | captcha.createCode(); |
| | | String code = captcha.getCode(); |
| | | if (isMath) { |
| | | ExpressionParser parser = new SpelExpressionParser(); |
| | | Expression exp = parser.parseExpression(StringUtils.remove(code, "=")); |
| | | code = exp.getValue(String.class); |
| | | } |
| | | RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); |
| | | captchaVo.setUuid(uuid); |
| | | captchaVo.setImg(captcha.getImageBase64()); |
| | | return R.ok(captchaVo); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.controller; |
| | | |
| | | import cn.dev33.satoken.annotation.SaIgnore; |
| | | import com.sunsail.common.core.config.RuoYiConfig; |
| | | import com.sunsail.common.core.utils.StringUtils; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * 首页 |
| | | * |
| | | * @author Lion Li |
| | | */ |
| | | @SaIgnore |
| | | @RequiredArgsConstructor |
| | | @RestController |
| | | public class IndexController { |
| | | |
| | | /** |
| | | * 系统基础配置 |
| | | */ |
| | | private final RuoYiConfig ruoyiConfig; |
| | | |
| | | /** |
| | | * 访问首页,提示语 |
| | | */ |
| | | @GetMapping("/") |
| | | public String index() { |
| | | return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.domain.vo; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 验证码信息 |
| | | * |
| | | * @author Michelle.Chung |
| | | */ |
| | | @Data |
| | | public class CaptchaVo { |
| | | |
| | | /** |
| | | * 是否开启验证码 |
| | | */ |
| | | private Boolean captchaEnabled = true; |
| | | |
| | | private String uuid; |
| | | |
| | | /** |
| | | * 验证码图片 |
| | | */ |
| | | private String img; |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.domain.vo; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 登录租户对象 |
| | | * |
| | | * @author Michelle.Chung |
| | | */ |
| | | @Data |
| | | public class LoginTenantVo { |
| | | |
| | | /** |
| | | * 租户开关 |
| | | */ |
| | | private Boolean tenantEnabled; |
| | | |
| | | /** |
| | | * 租户对象列表 |
| | | */ |
| | | private List<TenantListVo> voList; |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.domain.vo; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 登录验证信息 |
| | | * |
| | | * @author Michelle.Chung |
| | | */ |
| | | @Data |
| | | public class LoginVo { |
| | | |
| | | /** |
| | | * 授权令牌 |
| | | */ |
| | | @JsonProperty("access_token") |
| | | private String accessToken; |
| | | |
| | | /** |
| | | * 刷新令牌 |
| | | */ |
| | | @JsonProperty("refresh_token") |
| | | private String refreshToken; |
| | | |
| | | /** |
| | | * 授权令牌 access_token 的有效期 |
| | | */ |
| | | @JsonProperty("expire_in") |
| | | private Long expireIn; |
| | | |
| | | /** |
| | | * 刷新令牌 refresh_token 的有效期 |
| | | */ |
| | | @JsonProperty("refresh_expire_in") |
| | | private Long refreshExpireIn; |
| | | |
| | | /** |
| | | * 应用id |
| | | */ |
| | | @JsonProperty("client_id") |
| | | private String clientId; |
| | | |
| | | /** |
| | | * 令牌权限 |
| | | */ |
| | | private String scope; |
| | | |
| | | /** |
| | | * 用户 openid |
| | | */ |
| | | private String openid; |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.domain.vo; |
| | | |
| | | import com.sunsail.system.domain.vo.SysTenantVo; |
| | | import io.github.linpeilie.annotations.AutoMapper; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 租户列表 |
| | | * |
| | | * @author Lion Li |
| | | */ |
| | | @Data |
| | | @AutoMapper(target = SysTenantVo.class) |
| | | public class TenantListVo { |
| | | |
| | | private String tenantId; |
| | | |
| | | private String companyName; |
| | | |
| | | private String domain; |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.listener; |
| | | |
| | | import cn.dev33.satoken.config.SaTokenConfig; |
| | | import cn.dev33.satoken.listener.SaTokenListener; |
| | | import cn.dev33.satoken.stp.SaLoginModel; |
| | | import cn.hutool.http.useragent.UserAgent; |
| | | import cn.hutool.http.useragent.UserAgentUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import com.sunsail.common.core.constant.CacheConstants; |
| | | import com.sunsail.common.core.constant.Constants; |
| | | import com.sunsail.common.core.domain.dto.UserOnlineDTO; |
| | | import com.sunsail.common.core.domain.model.LoginUser; |
| | | import com.sunsail.common.core.utils.MessageUtils; |
| | | import com.sunsail.common.core.utils.ServletUtils; |
| | | import com.sunsail.common.core.utils.SpringUtils; |
| | | import com.sunsail.common.core.utils.ip.AddressUtils; |
| | | import com.sunsail.common.log.event.LogininforEvent; |
| | | import com.sunsail.common.redis.utils.RedisUtils; |
| | | import com.sunsail.common.satoken.utils.LoginHelper; |
| | | import com.sunsail.web.service.SysLoginService; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.time.Duration; |
| | | |
| | | /** |
| | | * 用户行为 侦听器的实现 |
| | | * |
| | | * @author Lion Li |
| | | */ |
| | | @RequiredArgsConstructor |
| | | @Component |
| | | @Slf4j |
| | | public class UserActionListener implements SaTokenListener { |
| | | |
| | | private final SaTokenConfig tokenConfig; |
| | | private final SysLoginService loginService; |
| | | |
| | | /** |
| | | * 每次登录时触发 |
| | | */ |
| | | @Override |
| | | public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) { |
| | | UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); |
| | | String ip = ServletUtils.getClientIP(); |
| | | LoginUser user = LoginHelper.getLoginUser(); |
| | | UserOnlineDTO dto = new UserOnlineDTO(); |
| | | dto.setIpaddr(ip); |
| | | dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); |
| | | dto.setBrowser(userAgent.getBrowser().getName()); |
| | | dto.setOs(userAgent.getOs().getName()); |
| | | dto.setLoginTime(System.currentTimeMillis()); |
| | | dto.setTokenId(tokenValue); |
| | | dto.setUserName(user.getUsername()); |
| | | dto.setClientKey(user.getClientKey()); |
| | | dto.setDeviceType(user.getDeviceType()); |
| | | dto.setDeptName(user.getDeptName()); |
| | | if(tokenConfig.getTimeout() == -1) { |
| | | RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); |
| | | } else { |
| | | RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout())); |
| | | } |
| | | // 记录登录日志 |
| | | LogininforEvent logininforEvent = new LogininforEvent(); |
| | | logininforEvent.setTenantId(user.getTenantId()); |
| | | logininforEvent.setUsername(user.getUsername()); |
| | | logininforEvent.setStatus(Constants.LOGIN_SUCCESS); |
| | | logininforEvent.setMessage(MessageUtils.message("user.login.success")); |
| | | logininforEvent.setRequest(ServletUtils.getRequest()); |
| | | SpringUtils.context().publishEvent(logininforEvent); |
| | | // 更新登录信息 |
| | | loginService.recordLoginInfo(user.getUserId(), ip); |
| | | log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); |
| | | } |
| | | |
| | | /** |
| | | * 每次注销时触发 |
| | | */ |
| | | @Override |
| | | public void doLogout(String loginType, Object loginId, String tokenValue) { |
| | | RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); |
| | | log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue); |
| | | } |
| | | |
| | | /** |
| | | * 每次被踢下线时触发 |
| | | */ |
| | | @Override |
| | | public void doKickout(String loginType, Object loginId, String tokenValue) { |
| | | RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); |
| | | log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue); |
| | | } |
| | | |
| | | /** |
| | | * 每次被顶下线时触发 |
| | | */ |
| | | @Override |
| | | public void doReplaced(String loginType, Object loginId, String tokenValue) { |
| | | RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); |
| | | log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue); |
| | | } |
| | | |
| | | /** |
| | | * 每次被封禁时触发 |
| | | */ |
| | | @Override |
| | | public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) { |
| | | } |
| | | |
| | | /** |
| | | * 每次被解封时触发 |
| | | */ |
| | | @Override |
| | | public void doUntieDisable(String loginType, Object loginId, String service) { |
| | | } |
| | | |
| | | /** |
| | | * 每次打开二级认证时触发 |
| | | */ |
| | | @Override |
| | | public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) { |
| | | } |
| | | |
| | | /** |
| | | * 每次创建Session时触发 |
| | | */ |
| | | @Override |
| | | public void doCloseSafe(String loginType, String tokenValue, String service) { |
| | | } |
| | | |
| | | /** |
| | | * 每次创建Session时触发 |
| | | */ |
| | | @Override |
| | | public void doCreateSession(String id) { |
| | | } |
| | | |
| | | /** |
| | | * 每次注销Session时触发 |
| | | */ |
| | | @Override |
| | | public void doLogoutSession(String id) { |
| | | } |
| | | |
| | | /** |
| | | * 每次Token续期时触发 |
| | | */ |
| | | @Override |
| | | public void doRenewTimeout(String tokenValue, Object loginId, long timeout) { |
| | | } |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.service; |
| | | |
| | | |
| | | import com.sunsail.common.core.exception.ServiceException; |
| | | import com.sunsail.common.core.utils.SpringUtils; |
| | | import com.sunsail.system.domain.SysClient; |
| | | import com.sunsail.web.domain.vo.LoginVo; |
| | | |
| | | /** |
| | | * 授权策略 |
| | | * |
| | | * @author Michelle.Chung |
| | | */ |
| | | public interface IAuthStrategy { |
| | | |
| | | String BASE_NAME = "AuthStrategy"; |
| | | |
| | | /** |
| | | * 登录 |
| | | */ |
| | | static LoginVo login(String body, SysClient client, String grantType) { |
| | | // 授权类型和客户端id |
| | | String beanName = grantType + BASE_NAME; |
| | | if (!SpringUtils.containsBean(beanName)) { |
| | | throw new ServiceException("授权类型不正确!"); |
| | | } |
| | | IAuthStrategy instance = SpringUtils.getBean(beanName); |
| | | return instance.login(body, client); |
| | | } |
| | | |
| | | /** |
| | | * 登录 |
| | | */ |
| | | LoginVo login(String body, SysClient client); |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.service; |
| | | |
| | | import cn.dev33.satoken.exception.NotLoginException; |
| | | import cn.dev33.satoken.stp.StpUtil; |
| | | import cn.hutool.core.bean.BeanUtil; |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import me.zhyd.oauth.model.AuthUser; |
| | | import com.sunsail.common.core.constant.Constants; |
| | | import com.sunsail.common.core.constant.GlobalConstants; |
| | | import com.sunsail.common.core.constant.TenantConstants; |
| | | import com.sunsail.common.core.domain.dto.RoleDTO; |
| | | import com.sunsail.common.core.domain.model.LoginUser; |
| | | import com.sunsail.common.core.enums.LoginType; |
| | | import com.sunsail.common.core.enums.TenantStatus; |
| | | import com.sunsail.common.core.exception.user.UserException; |
| | | import com.sunsail.common.core.utils.*; |
| | | import com.sunsail.common.log.event.LogininforEvent; |
| | | import com.sunsail.common.mybatis.helper.DataPermissionHelper; |
| | | import com.sunsail.common.redis.utils.RedisUtils; |
| | | import com.sunsail.common.satoken.utils.LoginHelper; |
| | | import com.sunsail.common.tenant.exception.TenantException; |
| | | import com.sunsail.common.tenant.helper.TenantHelper; |
| | | import com.sunsail.system.domain.SysUser; |
| | | import com.sunsail.system.domain.bo.SysSocialBo; |
| | | import com.sunsail.system.domain.vo.SysSocialVo; |
| | | import com.sunsail.system.domain.vo.SysTenantVo; |
| | | import com.sunsail.system.domain.vo.SysUserVo; |
| | | import com.sunsail.system.mapper.SysUserMapper; |
| | | import com.sunsail.system.service.ISysPermissionService; |
| | | import com.sunsail.system.service.ISysSocialService; |
| | | import com.sunsail.system.service.ISysTenantService; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.time.Duration; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.function.Supplier; |
| | | |
| | | /** |
| | | * 登录校验方法 |
| | | * |
| | | * @author Lion Li |
| | | */ |
| | | @RequiredArgsConstructor |
| | | @Slf4j |
| | | @Service |
| | | public class SysLoginService { |
| | | |
| | | @Value("${user.password.maxRetryCount}") |
| | | private Integer maxRetryCount; |
| | | |
| | | @Value("${user.password.lockTime}") |
| | | private Integer lockTime; |
| | | |
| | | private final ISysTenantService tenantService; |
| | | private final ISysPermissionService permissionService; |
| | | private final ISysSocialService sysSocialService; |
| | | private final SysUserMapper userMapper; |
| | | |
| | | |
| | | /** |
| | | * 绑定第三方用户 |
| | | * |
| | | * @param authUserData 授权响应实体 |
| | | * @return 统一响应实体 |
| | | */ |
| | | public void socialRegister(AuthUser authUserData) { |
| | | String authId = authUserData.getSource() + authUserData.getUuid(); |
| | | // 第三方用户信息 |
| | | SysSocialBo bo = BeanUtil.toBean(authUserData, SysSocialBo.class); |
| | | BeanUtil.copyProperties(authUserData.getToken(), bo); |
| | | bo.setUserId(LoginHelper.getUserId()); |
| | | bo.setAuthId(authId); |
| | | bo.setOpenId(authUserData.getUuid()); |
| | | bo.setUserName(authUserData.getUsername()); |
| | | bo.setNickName(authUserData.getNickname()); |
| | | // 查询是否已经绑定用户 |
| | | List<SysSocialVo> list = sysSocialService.selectByAuthId(authId); |
| | | if (CollUtil.isEmpty(list)) { |
| | | // 没有绑定用户, 新增用户信息 |
| | | sysSocialService.insertByBo(bo); |
| | | } else { |
| | | // 更新用户信息 |
| | | bo.setId(list.get(0).getId()); |
| | | sysSocialService.updateByBo(bo); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 退出登录 |
| | | */ |
| | | public void logout() { |
| | | try { |
| | | LoginUser loginUser = LoginHelper.getLoginUser(); |
| | | if (ObjectUtil.isNull(loginUser)) { |
| | | return; |
| | | } |
| | | if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) { |
| | | // 超级管理员 登出清除动态租户 |
| | | TenantHelper.clearDynamic(); |
| | | } |
| | | recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success")); |
| | | } catch (NotLoginException ignored) { |
| | | } finally { |
| | | try { |
| | | StpUtil.logout(); |
| | | } catch (NotLoginException ignored) { |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 记录登录信息 |
| | | * |
| | | * @param tenantId 租户ID |
| | | * @param username 用户名 |
| | | * @param status 状态 |
| | | * @param message 消息内容 |
| | | */ |
| | | public void recordLogininfor(String tenantId, String username, String status, String message) { |
| | | LogininforEvent logininforEvent = new LogininforEvent(); |
| | | logininforEvent.setTenantId(tenantId); |
| | | logininforEvent.setUsername(username); |
| | | logininforEvent.setStatus(status); |
| | | logininforEvent.setMessage(message); |
| | | logininforEvent.setRequest(ServletUtils.getRequest()); |
| | | SpringUtils.context().publishEvent(logininforEvent); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 构建登录用户 |
| | | */ |
| | | public LoginUser buildLoginUser(SysUserVo user) { |
| | | LoginUser loginUser = new LoginUser(); |
| | | loginUser.setTenantId(user.getTenantId()); |
| | | loginUser.setUserId(user.getUserId()); |
| | | loginUser.setDeptId(user.getDeptId()); |
| | | loginUser.setUsername(user.getUserName()); |
| | | loginUser.setNickname(user.getNickName()); |
| | | loginUser.setUserType(user.getUserType()); |
| | | loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId())); |
| | | loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId())); |
| | | loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? "" : user.getDept().getDeptName()); |
| | | List<RoleDTO> roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class); |
| | | loginUser.setRoles(roles); |
| | | return loginUser; |
| | | } |
| | | |
| | | /** |
| | | * 记录登录信息 |
| | | * |
| | | * @param userId 用户ID |
| | | */ |
| | | public void recordLoginInfo(Long userId, String ip) { |
| | | SysUser sysUser = new SysUser(); |
| | | sysUser.setUserId(userId); |
| | | sysUser.setLoginIp(ip); |
| | | sysUser.setLoginDate(DateUtils.getNowDate()); |
| | | sysUser.setUpdateBy(userId); |
| | | DataPermissionHelper.ignore(() -> userMapper.updateById(sysUser)); |
| | | } |
| | | |
| | | /** |
| | | * 登录校验 |
| | | */ |
| | | public void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) { |
| | | String errorKey = GlobalConstants.PWD_ERR_CNT_KEY + username; |
| | | String loginFail = Constants.LOGIN_FAIL; |
| | | |
| | | // 获取用户登录错误次数,默认为0 (可自定义限制策略 例如: key + username + ip) |
| | | int errorNumber = ObjectUtil.defaultIfNull(RedisUtils.getCacheObject(errorKey), 0); |
| | | // 锁定时间内登录 则踢出 |
| | | if (errorNumber >= maxRetryCount) { |
| | | recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); |
| | | throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); |
| | | } |
| | | |
| | | if (supplier.get()) { |
| | | // 错误次数递增 |
| | | errorNumber++; |
| | | RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime)); |
| | | // 达到规定错误次数 则锁定登录 |
| | | if (errorNumber >= maxRetryCount) { |
| | | recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); |
| | | throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); |
| | | } else { |
| | | // 未达到规定错误次数 |
| | | recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber)); |
| | | throw new UserException(loginType.getRetryLimitCount(), errorNumber); |
| | | } |
| | | } |
| | | |
| | | // 登录成功 清空错误次数 |
| | | RedisUtils.deleteObject(errorKey); |
| | | } |
| | | |
| | | /** |
| | | * 校验租户 |
| | | * |
| | | * @param tenantId 租户ID |
| | | */ |
| | | public void checkTenant(String tenantId) { |
| | | if (!TenantHelper.isEnable()) { |
| | | return; |
| | | } |
| | | if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) { |
| | | return; |
| | | } |
| | | if (StringUtils.isBlank(tenantId)) { |
| | | throw new TenantException("tenant.number.not.blank"); |
| | | } |
| | | SysTenantVo tenant = tenantService.queryByTenantId(tenantId); |
| | | if (ObjectUtil.isNull(tenant)) { |
| | | log.info("登录租户:{} 不存在.", tenantId); |
| | | throw new TenantException("tenant.not.exists"); |
| | | } else if (TenantStatus.DISABLE.getCode().equals(tenant.getStatus())) { |
| | | log.info("登录租户:{} 已被停用.", tenantId); |
| | | throw new TenantException("tenant.blocked"); |
| | | } else if (ObjectUtil.isNotNull(tenant.getExpireTime()) |
| | | && new Date().after(tenant.getExpireTime())) { |
| | | log.info("登录租户:{} 已超过有效期.", tenantId); |
| | | throw new TenantException("tenant.expired"); |
| | | } |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.service; |
| | | |
| | | import cn.dev33.satoken.secure.BCrypt; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import lombok.RequiredArgsConstructor; |
| | | import com.sunsail.common.core.constant.Constants; |
| | | import com.sunsail.common.core.constant.GlobalConstants; |
| | | import com.sunsail.common.core.domain.model.RegisterBody; |
| | | import com.sunsail.common.core.enums.UserType; |
| | | import com.sunsail.common.core.exception.user.CaptchaException; |
| | | import com.sunsail.common.core.exception.user.CaptchaExpireException; |
| | | import com.sunsail.common.core.exception.user.UserException; |
| | | import com.sunsail.common.core.utils.MessageUtils; |
| | | import com.sunsail.common.core.utils.ServletUtils; |
| | | import com.sunsail.common.core.utils.SpringUtils; |
| | | import com.sunsail.common.core.utils.StringUtils; |
| | | import com.sunsail.common.log.event.LogininforEvent; |
| | | import com.sunsail.common.redis.utils.RedisUtils; |
| | | import com.sunsail.common.tenant.helper.TenantHelper; |
| | | import com.sunsail.common.web.config.properties.CaptchaProperties; |
| | | import com.sunsail.system.domain.SysUser; |
| | | import com.sunsail.system.domain.bo.SysUserBo; |
| | | import com.sunsail.system.mapper.SysUserMapper; |
| | | import com.sunsail.system.service.ISysUserService; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * 注册校验方法 |
| | | * |
| | | * @author Lion Li |
| | | */ |
| | | @RequiredArgsConstructor |
| | | @Service |
| | | public class SysRegisterService { |
| | | |
| | | private final ISysUserService userService; |
| | | private final SysUserMapper userMapper; |
| | | private final CaptchaProperties captchaProperties; |
| | | |
| | | /** |
| | | * 注册 |
| | | */ |
| | | public void register(RegisterBody registerBody) { |
| | | String tenantId = registerBody.getTenantId(); |
| | | String username = registerBody.getUsername(); |
| | | String password = registerBody.getPassword(); |
| | | // 校验用户类型是否存在 |
| | | String userType = UserType.getUserType(registerBody.getUserType()).getUserType(); |
| | | |
| | | boolean captchaEnabled = captchaProperties.getEnable(); |
| | | // 验证码开关 |
| | | if (captchaEnabled) { |
| | | validateCaptcha(tenantId, username, registerBody.getCode(), registerBody.getUuid()); |
| | | } |
| | | SysUserBo sysUser = new SysUserBo(); |
| | | sysUser.setUserName(username); |
| | | sysUser.setNickName(username); |
| | | sysUser.setPassword(BCrypt.hashpw(password)); |
| | | sysUser.setUserType(userType); |
| | | |
| | | boolean exist = TenantHelper.dynamic(tenantId, () -> { |
| | | return userMapper.exists(new LambdaQueryWrapper<SysUser>() |
| | | .eq(SysUser::getUserName, sysUser.getUserName()) |
| | | .ne(ObjectUtil.isNotNull(sysUser.getUserId()), SysUser::getUserId, sysUser.getUserId())); |
| | | }); |
| | | if (exist) { |
| | | throw new UserException("user.register.save.error", username); |
| | | } |
| | | boolean regFlag = userService.registerUser(sysUser, tenantId); |
| | | if (!regFlag) { |
| | | throw new UserException("user.register.error"); |
| | | } |
| | | recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success")); |
| | | } |
| | | |
| | | /** |
| | | * 校验验证码 |
| | | * |
| | | * @param username 用户名 |
| | | * @param code 验证码 |
| | | * @param uuid 唯一标识 |
| | | */ |
| | | public void validateCaptcha(String tenantId, String username, String code, String uuid) { |
| | | String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, ""); |
| | | String captcha = RedisUtils.getCacheObject(verifyKey); |
| | | RedisUtils.deleteObject(verifyKey); |
| | | if (captcha == null) { |
| | | recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire")); |
| | | throw new CaptchaExpireException(); |
| | | } |
| | | if (!code.equalsIgnoreCase(captcha)) { |
| | | recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error")); |
| | | throw new CaptchaException(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 记录登录信息 |
| | | * |
| | | * @param tenantId 租户ID |
| | | * @param username 用户名 |
| | | * @param status 状态 |
| | | * @param message 消息内容 |
| | | * @return |
| | | */ |
| | | private void recordLogininfor(String tenantId, String username, String status, String message) { |
| | | LogininforEvent logininforEvent = new LogininforEvent(); |
| | | logininforEvent.setTenantId(tenantId); |
| | | logininforEvent.setUsername(username); |
| | | logininforEvent.setStatus(status); |
| | | logininforEvent.setMessage(message); |
| | | logininforEvent.setRequest(ServletUtils.getRequest()); |
| | | SpringUtils.context().publishEvent(logininforEvent); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.service.impl; |
| | | |
| | | import cn.dev33.satoken.stp.SaLoginModel; |
| | | import cn.dev33.satoken.stp.StpUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import com.sunsail.common.core.constant.Constants; |
| | | import com.sunsail.common.core.constant.GlobalConstants; |
| | | import com.sunsail.common.core.domain.model.EmailLoginBody; |
| | | import com.sunsail.common.core.domain.model.LoginUser; |
| | | import com.sunsail.common.core.enums.LoginType; |
| | | import com.sunsail.common.core.enums.UserStatus; |
| | | import com.sunsail.common.core.exception.user.CaptchaExpireException; |
| | | import com.sunsail.common.core.exception.user.UserException; |
| | | import com.sunsail.common.core.utils.MessageUtils; |
| | | import com.sunsail.common.core.utils.StringUtils; |
| | | import com.sunsail.common.core.utils.ValidatorUtils; |
| | | import com.sunsail.common.json.utils.JsonUtils; |
| | | import com.sunsail.common.redis.utils.RedisUtils; |
| | | import com.sunsail.common.satoken.utils.LoginHelper; |
| | | import com.sunsail.common.tenant.helper.TenantHelper; |
| | | import com.sunsail.system.domain.SysClient; |
| | | import com.sunsail.system.domain.SysUser; |
| | | import com.sunsail.system.domain.vo.SysUserVo; |
| | | import com.sunsail.system.mapper.SysUserMapper; |
| | | import com.sunsail.web.domain.vo.LoginVo; |
| | | import com.sunsail.web.service.IAuthStrategy; |
| | | import com.sunsail.web.service.SysLoginService; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * 邮件认证策略 |
| | | * |
| | | * @author Michelle.Chung |
| | | */ |
| | | @Slf4j |
| | | @Service("email" + IAuthStrategy.BASE_NAME) |
| | | @RequiredArgsConstructor |
| | | public class EmailAuthStrategy implements IAuthStrategy { |
| | | |
| | | private final SysLoginService loginService; |
| | | private final SysUserMapper userMapper; |
| | | |
| | | @Override |
| | | public LoginVo login(String body, SysClient client) { |
| | | EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class); |
| | | ValidatorUtils.validate(loginBody); |
| | | String tenantId = loginBody.getTenantId(); |
| | | String email = loginBody.getEmail(); |
| | | String emailCode = loginBody.getEmailCode(); |
| | | |
| | | // 通过邮箱查找用户 |
| | | SysUserVo user = loadUserByEmail(tenantId, email); |
| | | |
| | | loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode)); |
| | | // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 |
| | | LoginUser loginUser = loginService.buildLoginUser(user); |
| | | loginUser.setClientKey(client.getClientKey()); |
| | | loginUser.setDeviceType(client.getDeviceType()); |
| | | SaLoginModel model = new SaLoginModel(); |
| | | model.setDevice(client.getDeviceType()); |
| | | // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 |
| | | // 例如: 后台用户30分钟过期 app用户1天过期 |
| | | model.setTimeout(client.getTimeout()); |
| | | model.setActiveTimeout(client.getActiveTimeout()); |
| | | model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); |
| | | // 生成token |
| | | LoginHelper.login(loginUser, model); |
| | | |
| | | LoginVo loginVo = new LoginVo(); |
| | | loginVo.setAccessToken(StpUtil.getTokenValue()); |
| | | loginVo.setExpireIn(StpUtil.getTokenTimeout()); |
| | | loginVo.setClientId(client.getClientId()); |
| | | return loginVo; |
| | | } |
| | | |
| | | /** |
| | | * 校验邮箱验证码 |
| | | */ |
| | | private boolean validateEmailCode(String tenantId, String email, String emailCode) { |
| | | String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email); |
| | | if (StringUtils.isBlank(code)) { |
| | | loginService.recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); |
| | | throw new CaptchaExpireException(); |
| | | } |
| | | return code.equals(emailCode); |
| | | } |
| | | |
| | | private SysUserVo loadUserByEmail(String tenantId, String email) { |
| | | return TenantHelper.dynamic(tenantId, () -> { |
| | | SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>() |
| | | .select(SysUser::getEmail, SysUser::getStatus) |
| | | .eq(SysUser::getEmail, email)); |
| | | if (ObjectUtil.isNull(user)) { |
| | | log.info("登录用户:{} 不存在.", email); |
| | | throw new UserException("user.not.exists", email); |
| | | } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { |
| | | log.info("登录用户:{} 已被停用.", email); |
| | | throw new UserException("user.blocked", email); |
| | | } |
| | | return userMapper.selectUserByEmail(email); |
| | | }); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.service.impl; |
| | | |
| | | import cn.dev33.satoken.secure.BCrypt; |
| | | import cn.dev33.satoken.stp.SaLoginModel; |
| | | import cn.dev33.satoken.stp.StpUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import com.sunsail.common.core.constant.Constants; |
| | | import com.sunsail.common.core.constant.GlobalConstants; |
| | | import com.sunsail.common.core.domain.model.LoginUser; |
| | | import com.sunsail.common.core.domain.model.PasswordLoginBody; |
| | | import com.sunsail.common.core.enums.LoginType; |
| | | import com.sunsail.common.core.enums.UserStatus; |
| | | import com.sunsail.common.core.exception.user.CaptchaException; |
| | | import com.sunsail.common.core.exception.user.CaptchaExpireException; |
| | | import com.sunsail.common.core.exception.user.UserException; |
| | | import com.sunsail.common.core.utils.MessageUtils; |
| | | import com.sunsail.common.core.utils.StringUtils; |
| | | import com.sunsail.common.core.utils.ValidatorUtils; |
| | | import com.sunsail.common.json.utils.JsonUtils; |
| | | import com.sunsail.common.redis.utils.RedisUtils; |
| | | import com.sunsail.common.satoken.utils.LoginHelper; |
| | | import com.sunsail.common.tenant.helper.TenantHelper; |
| | | import com.sunsail.common.web.config.properties.CaptchaProperties; |
| | | import com.sunsail.system.domain.SysClient; |
| | | import com.sunsail.system.domain.SysUser; |
| | | import com.sunsail.system.domain.vo.SysUserVo; |
| | | import com.sunsail.system.mapper.SysUserMapper; |
| | | import com.sunsail.web.domain.vo.LoginVo; |
| | | import com.sunsail.web.service.IAuthStrategy; |
| | | import com.sunsail.web.service.SysLoginService; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * 密码认证策略 |
| | | * |
| | | * @author Michelle.Chung |
| | | */ |
| | | @Slf4j |
| | | @Service("password" + IAuthStrategy.BASE_NAME) |
| | | @RequiredArgsConstructor |
| | | public class PasswordAuthStrategy implements IAuthStrategy { |
| | | |
| | | private final CaptchaProperties captchaProperties; |
| | | private final SysLoginService loginService; |
| | | private final SysUserMapper userMapper; |
| | | |
| | | @Override |
| | | public LoginVo login(String body, SysClient client) { |
| | | PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class); |
| | | ValidatorUtils.validate(loginBody); |
| | | String tenantId = loginBody.getTenantId(); |
| | | String username = loginBody.getUsername(); |
| | | String password = loginBody.getPassword(); |
| | | String code = loginBody.getCode(); |
| | | String uuid = loginBody.getUuid(); |
| | | |
| | | boolean captchaEnabled = captchaProperties.getEnable(); |
| | | // 验证码开关 |
| | | if (captchaEnabled) { |
| | | validateCaptcha(tenantId, username, code, uuid); |
| | | } |
| | | |
| | | SysUserVo user = loadUserByUsername(tenantId, username); |
| | | loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword())); |
| | | // 此处可根据登录用户的数据不同 自行创建 loginUser |
| | | LoginUser loginUser = loginService.buildLoginUser(user); |
| | | loginUser.setClientKey(client.getClientKey()); |
| | | loginUser.setDeviceType(client.getDeviceType()); |
| | | SaLoginModel model = new SaLoginModel(); |
| | | model.setDevice(client.getDeviceType()); |
| | | // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 |
| | | // 例如: 后台用户30分钟过期 app用户1天过期 |
| | | model.setTimeout(client.getTimeout()); |
| | | model.setActiveTimeout(client.getActiveTimeout()); |
| | | model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); |
| | | // 生成token |
| | | LoginHelper.login(loginUser, model); |
| | | |
| | | LoginVo loginVo = new LoginVo(); |
| | | loginVo.setAccessToken(StpUtil.getTokenValue()); |
| | | loginVo.setExpireIn(StpUtil.getTokenTimeout()); |
| | | loginVo.setClientId(client.getClientId()); |
| | | return loginVo; |
| | | } |
| | | |
| | | /** |
| | | * 校验验证码 |
| | | * |
| | | * @param username 用户名 |
| | | * @param code 验证码 |
| | | * @param uuid 唯一标识 |
| | | */ |
| | | private void validateCaptcha(String tenantId, String username, String code, String uuid) { |
| | | String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, ""); |
| | | String captcha = RedisUtils.getCacheObject(verifyKey); |
| | | RedisUtils.deleteObject(verifyKey); |
| | | if (captcha == null) { |
| | | loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); |
| | | throw new CaptchaExpireException(); |
| | | } |
| | | if (!code.equalsIgnoreCase(captcha)) { |
| | | loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")); |
| | | throw new CaptchaException(); |
| | | } |
| | | } |
| | | |
| | | private SysUserVo loadUserByUsername(String tenantId, String username) { |
| | | return TenantHelper.dynamic(tenantId, () -> { |
| | | SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>() |
| | | .select(SysUser::getUserName, SysUser::getStatus) |
| | | .eq(SysUser::getUserName, username)); |
| | | if (ObjectUtil.isNull(user)) { |
| | | log.info("登录用户:{} 不存在.", username); |
| | | throw new UserException("user.not.exists", username); |
| | | } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { |
| | | log.info("登录用户:{} 已被停用.", username); |
| | | throw new UserException("user.blocked", username); |
| | | } |
| | | return userMapper.selectUserByUserName(username); |
| | | }); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.service.impl; |
| | | |
| | | import cn.dev33.satoken.stp.SaLoginModel; |
| | | import cn.dev33.satoken.stp.StpUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import com.sunsail.common.core.constant.Constants; |
| | | import com.sunsail.common.core.constant.GlobalConstants; |
| | | import com.sunsail.common.core.domain.model.LoginUser; |
| | | import com.sunsail.common.core.domain.model.SmsLoginBody; |
| | | import com.sunsail.common.core.enums.LoginType; |
| | | import com.sunsail.common.core.enums.UserStatus; |
| | | import com.sunsail.common.core.exception.user.CaptchaExpireException; |
| | | import com.sunsail.common.core.exception.user.UserException; |
| | | import com.sunsail.common.core.utils.MessageUtils; |
| | | import com.sunsail.common.core.utils.StringUtils; |
| | | import com.sunsail.common.core.utils.ValidatorUtils; |
| | | import com.sunsail.common.json.utils.JsonUtils; |
| | | import com.sunsail.common.redis.utils.RedisUtils; |
| | | import com.sunsail.common.satoken.utils.LoginHelper; |
| | | import com.sunsail.common.tenant.helper.TenantHelper; |
| | | import com.sunsail.system.domain.SysClient; |
| | | import com.sunsail.system.domain.SysUser; |
| | | import com.sunsail.system.domain.vo.SysUserVo; |
| | | import com.sunsail.system.mapper.SysUserMapper; |
| | | import com.sunsail.web.domain.vo.LoginVo; |
| | | import com.sunsail.web.service.IAuthStrategy; |
| | | import com.sunsail.web.service.SysLoginService; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * 短信认证策略 |
| | | * |
| | | * @author Michelle.Chung |
| | | */ |
| | | @Slf4j |
| | | @Service("sms" + IAuthStrategy.BASE_NAME) |
| | | @RequiredArgsConstructor |
| | | public class SmsAuthStrategy implements IAuthStrategy { |
| | | |
| | | private final SysLoginService loginService; |
| | | private final SysUserMapper userMapper; |
| | | |
| | | @Override |
| | | public LoginVo login(String body, SysClient client) { |
| | | SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class); |
| | | ValidatorUtils.validate(loginBody); |
| | | String tenantId = loginBody.getTenantId(); |
| | | String phonenumber = loginBody.getPhonenumber(); |
| | | String smsCode = loginBody.getSmsCode(); |
| | | |
| | | // 通过手机号查找用户 |
| | | SysUserVo user = loadUserByPhonenumber(tenantId, phonenumber); |
| | | |
| | | loginService.checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode)); |
| | | // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 |
| | | LoginUser loginUser = loginService.buildLoginUser(user); |
| | | loginUser.setClientKey(client.getClientKey()); |
| | | loginUser.setDeviceType(client.getDeviceType()); |
| | | SaLoginModel model = new SaLoginModel(); |
| | | model.setDevice(client.getDeviceType()); |
| | | // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 |
| | | // 例如: 后台用户30分钟过期 app用户1天过期 |
| | | model.setTimeout(client.getTimeout()); |
| | | model.setActiveTimeout(client.getActiveTimeout()); |
| | | model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); |
| | | // 生成token |
| | | LoginHelper.login(loginUser, model); |
| | | |
| | | LoginVo loginVo = new LoginVo(); |
| | | loginVo.setAccessToken(StpUtil.getTokenValue()); |
| | | loginVo.setExpireIn(StpUtil.getTokenTimeout()); |
| | | loginVo.setClientId(client.getClientId()); |
| | | return loginVo; |
| | | } |
| | | |
| | | /** |
| | | * 校验短信验证码 |
| | | */ |
| | | private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) { |
| | | String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber); |
| | | if (StringUtils.isBlank(code)) { |
| | | loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); |
| | | throw new CaptchaExpireException(); |
| | | } |
| | | return code.equals(smsCode); |
| | | } |
| | | |
| | | private SysUserVo loadUserByPhonenumber(String tenantId, String phonenumber) { |
| | | return TenantHelper.dynamic(tenantId, () -> { |
| | | SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>() |
| | | .select(SysUser::getPhonenumber, SysUser::getStatus) |
| | | .eq(SysUser::getPhonenumber, phonenumber)); |
| | | if (ObjectUtil.isNull(user)) { |
| | | log.info("登录用户:{} 不存在.", phonenumber); |
| | | throw new UserException("user.not.exists", phonenumber); |
| | | } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { |
| | | log.info("登录用户:{} 已被停用.", phonenumber); |
| | | throw new UserException("user.blocked", phonenumber); |
| | | } |
| | | return userMapper.selectUserByPhonenumber(phonenumber); |
| | | }); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.service.impl; |
| | | |
| | | import cn.dev33.satoken.stp.SaLoginModel; |
| | | import cn.dev33.satoken.stp.StpUtil; |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.map.MapUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import cn.hutool.http.HttpUtil; |
| | | import cn.hutool.http.Method; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import me.zhyd.oauth.model.AuthResponse; |
| | | import me.zhyd.oauth.model.AuthUser; |
| | | import com.sunsail.common.core.domain.model.LoginUser; |
| | | import com.sunsail.common.core.domain.model.SocialLoginBody; |
| | | import com.sunsail.common.core.enums.UserStatus; |
| | | import com.sunsail.common.core.exception.ServiceException; |
| | | import com.sunsail.common.core.exception.user.UserException; |
| | | import com.sunsail.common.core.utils.ValidatorUtils; |
| | | import com.sunsail.common.json.utils.JsonUtils; |
| | | import com.sunsail.common.satoken.utils.LoginHelper; |
| | | import com.sunsail.common.social.config.properties.SocialProperties; |
| | | import com.sunsail.common.social.utils.SocialUtils; |
| | | import com.sunsail.common.tenant.helper.TenantHelper; |
| | | import com.sunsail.system.domain.SysClient; |
| | | import com.sunsail.system.domain.SysUser; |
| | | import com.sunsail.system.domain.vo.SysSocialVo; |
| | | import com.sunsail.system.domain.vo.SysUserVo; |
| | | import com.sunsail.system.mapper.SysUserMapper; |
| | | import com.sunsail.system.service.ISysSocialService; |
| | | import com.sunsail.web.domain.vo.LoginVo; |
| | | import com.sunsail.web.service.IAuthStrategy; |
| | | import com.sunsail.web.service.SysLoginService; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * 第三方授权策略 |
| | | * |
| | | * @author thiszhc is 三三 |
| | | */ |
| | | @Slf4j |
| | | @Service("social" + IAuthStrategy.BASE_NAME) |
| | | @RequiredArgsConstructor |
| | | public class SocialAuthStrategy implements IAuthStrategy { |
| | | |
| | | private final SocialProperties socialProperties; |
| | | private final ISysSocialService sysSocialService; |
| | | private final SysUserMapper userMapper; |
| | | private final SysLoginService loginService; |
| | | |
| | | /** |
| | | * 登录-第三方授权登录 |
| | | * |
| | | * @param body 登录信息 |
| | | * @param client 客户端信息 |
| | | */ |
| | | @Override |
| | | public LoginVo login(String body, SysClient client) { |
| | | SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class); |
| | | ValidatorUtils.validate(loginBody); |
| | | AuthResponse<AuthUser> response = SocialUtils.loginAuth( |
| | | loginBody.getSource(), loginBody.getSocialCode(), |
| | | loginBody.getSocialState(), socialProperties); |
| | | if (!response.ok()) { |
| | | throw new ServiceException(response.getMsg()); |
| | | } |
| | | AuthUser authUserData = response.getData(); |
| | | if ("GITEE".equals(authUserData.getSource())) { |
| | | // 如用户使用 gitee 登录顺手 star 给作者一点支持 拒绝白嫖 |
| | | HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Vue-Plus") |
| | | .formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken())) |
| | | .executeAsync(); |
| | | HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Cloud-Plus") |
| | | .formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken())) |
| | | .executeAsync(); |
| | | } |
| | | |
| | | List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid()); |
| | | if (CollUtil.isEmpty(list)) { |
| | | throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!"); |
| | | } |
| | | Optional<SysSocialVo> opt = list.stream().filter(x -> x.getTenantId().equals(loginBody.getTenantId())).findAny(); |
| | | if (opt.isEmpty()) { |
| | | throw new ServiceException("对不起,你没有权限登录当前租户!"); |
| | | } |
| | | SysSocialVo social = opt.get(); |
| | | // 查找用户 |
| | | SysUserVo user = loadUser(social.getTenantId(), social.getUserId()); |
| | | |
| | | // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 |
| | | LoginUser loginUser = loginService.buildLoginUser(user); |
| | | loginUser.setClientKey(client.getClientKey()); |
| | | loginUser.setDeviceType(client.getDeviceType()); |
| | | SaLoginModel model = new SaLoginModel(); |
| | | model.setDevice(client.getDeviceType()); |
| | | // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 |
| | | // 例如: 后台用户30分钟过期 app用户1天过期 |
| | | model.setTimeout(client.getTimeout()); |
| | | model.setActiveTimeout(client.getActiveTimeout()); |
| | | model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); |
| | | // 生成token |
| | | LoginHelper.login(loginUser, model); |
| | | |
| | | LoginVo loginVo = new LoginVo(); |
| | | loginVo.setAccessToken(StpUtil.getTokenValue()); |
| | | loginVo.setExpireIn(StpUtil.getTokenTimeout()); |
| | | loginVo.setClientId(client.getClientId()); |
| | | return loginVo; |
| | | } |
| | | |
| | | private SysUserVo loadUser(String tenantId, Long userId) { |
| | | return TenantHelper.dynamic(tenantId, () -> { |
| | | SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>() |
| | | .select(SysUser::getUserName, SysUser::getStatus) |
| | | .eq(SysUser::getUserId, userId)); |
| | | if (ObjectUtil.isNull(user)) { |
| | | log.info("登录用户:{} 不存在.", ""); |
| | | throw new UserException("user.not.exists", ""); |
| | | } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { |
| | | log.info("登录用户:{} 已被停用.", ""); |
| | | throw new UserException("user.blocked", ""); |
| | | } |
| | | return userMapper.selectUserByUserName(user.getUserName()); |
| | | }); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.sunsail.web.service.impl; |
| | | |
| | | import cn.dev33.satoken.stp.SaLoginModel; |
| | | import cn.dev33.satoken.stp.StpUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import com.sunsail.common.core.domain.model.XcxLoginBody; |
| | | import com.sunsail.common.core.domain.model.XcxLoginUser; |
| | | import com.sunsail.common.core.enums.UserStatus; |
| | | import com.sunsail.common.core.utils.ValidatorUtils; |
| | | import com.sunsail.common.json.utils.JsonUtils; |
| | | import com.sunsail.common.satoken.utils.LoginHelper; |
| | | import com.sunsail.system.domain.SysClient; |
| | | import com.sunsail.system.domain.vo.SysUserVo; |
| | | import com.sunsail.web.domain.vo.LoginVo; |
| | | import com.sunsail.web.service.IAuthStrategy; |
| | | import com.sunsail.web.service.SysLoginService; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * 邮件认证策略 |
| | | * |
| | | * @author Michelle.Chung |
| | | */ |
| | | @Slf4j |
| | | @Service("xcx" + IAuthStrategy.BASE_NAME) |
| | | @RequiredArgsConstructor |
| | | public class XcxAuthStrategy implements IAuthStrategy { |
| | | |
| | | private final SysLoginService loginService; |
| | | |
| | | @Override |
| | | public LoginVo login(String body, SysClient client) { |
| | | XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class); |
| | | ValidatorUtils.validate(loginBody); |
| | | // xcxCode 为 小程序调用 wx.login 授权后获取 |
| | | String xcxCode = loginBody.getXcxCode(); |
| | | // 多个小程序识别使用 |
| | | String appid = loginBody.getAppid(); |
| | | |
| | | // todo 以下自行实现 |
| | | // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid |
| | | String openid = ""; |
| | | // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可 |
| | | SysUserVo user = loadUserByOpenid(openid); |
| | | |
| | | // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 |
| | | XcxLoginUser loginUser = new XcxLoginUser(); |
| | | loginUser.setTenantId(user.getTenantId()); |
| | | loginUser.setUserId(user.getUserId()); |
| | | loginUser.setUsername(user.getUserName()); |
| | | loginUser.setNickname(user.getNickName()); |
| | | loginUser.setUserType(user.getUserType()); |
| | | loginUser.setClientKey(client.getClientKey()); |
| | | loginUser.setDeviceType(client.getDeviceType()); |
| | | loginUser.setOpenid(openid); |
| | | |
| | | SaLoginModel model = new SaLoginModel(); |
| | | model.setDevice(client.getDeviceType()); |
| | | // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 |
| | | // 例如: 后台用户30分钟过期 app用户1天过期 |
| | | model.setTimeout(client.getTimeout()); |
| | | model.setActiveTimeout(client.getActiveTimeout()); |
| | | model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); |
| | | // 生成token |
| | | LoginHelper.login(loginUser, model); |
| | | |
| | | LoginVo loginVo = new LoginVo(); |
| | | loginVo.setAccessToken(StpUtil.getTokenValue()); |
| | | loginVo.setExpireIn(StpUtil.getTokenTimeout()); |
| | | loginVo.setClientId(client.getClientId()); |
| | | loginVo.setOpenid(openid); |
| | | return loginVo; |
| | | } |
| | | |
| | | private SysUserVo loadUserByOpenid(String openid) { |
| | | // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户 |
| | | // todo 自行实现 userService.selectUserByOpenid(openid); |
| | | SysUserVo user = new SysUserVo(); |
| | | if (ObjectUtil.isNull(user)) { |
| | | log.info("登录用户:{} 不存在.", openid); |
| | | // todo 用户不存在 业务逻辑自行实现 |
| | | } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { |
| | | log.info("登录用户:{} 已被停用.", openid); |
| | | // todo 用户已被停用 业务逻辑自行实现 |
| | | } |
| | | return user; |
| | | } |
| | | |
| | | } |
| | |
| | | package org.dromara.test; |
| | | package com.sunsail.test; |
| | | |
| | | import org.junit.jupiter.api.Assertions; |
| | | import org.junit.jupiter.api.DisplayName; |
| | |
| | | package org.dromara.test; |
| | | package com.sunsail.test; |
| | | |
| | | import org.dromara.common.core.config.RuoYiConfig; |
| | | import com.sunsail.common.core.config.RuoYiConfig; |
| | | import org.junit.jupiter.api.*; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.boot.test.context.SpringBootTest; |
| | |
| | | package org.dromara.test; |
| | | package com.sunsail.test; |
| | | |
| | | import org.dromara.common.core.enums.UserType; |
| | | import com.sunsail.common.core.enums.UserType; |
| | | import org.junit.jupiter.api.AfterEach; |
| | | import org.junit.jupiter.api.BeforeEach; |
| | | import org.junit.jupiter.api.DisplayName; |
| | |
| | | package org.dromara.test; |
| | | package com.sunsail.test; |
| | | |
| | | import org.junit.jupiter.api.*; |
| | | import org.springframework.boot.test.context.SpringBootTest; |