package com.xjrsoft.auth.controller; import cn.dev33.satoken.SaManager; import cn.dev33.satoken.config.SaTokenConfig; import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.jwt.JWT; import cn.hutool.jwt.JWTUtil; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.xjrsoft.auth.dto.LoginDto; import com.xjrsoft.auth.service.ILoginService; import com.xjrsoft.auth.service.SsoService; import com.xjrsoft.common.core.annotation.XjrLog; import com.xjrsoft.common.core.config.KeyCloakConfig; import com.xjrsoft.common.core.config.XjrSmsConfig; import com.xjrsoft.common.core.constant.GlobalConstant; import com.xjrsoft.common.core.domain.result.R; import com.xjrsoft.common.core.enums.EnabledMark; import com.xjrsoft.common.core.enums.YesOrNoEnum; import com.xjrsoft.common.core.exception.MyException; import com.xjrsoft.common.core.uitls.SmsSender; import com.xjrsoft.common.redis.service.RedisUtil; import com.xjrsoft.organization.client.IOauthClient; import com.xjrsoft.organization.client.IUserClient; import com.xjrsoft.organization.entity.User; import com.xjrsoft.system.client.ILoginConfigClient; import com.xjrsoft.system.dto.*; import com.xjrsoft.system.entity.LoginConfig; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import me.zhyd.oauth.request.AuthRequest; import me.zhyd.oauth.utils.AuthStateUtils; import org.apache.commons.lang3.RandomUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.keycloak.authorization.client.AuthzClient; import org.keycloak.authorization.client.Configuration; import org.keycloak.representations.AccessTokenResponse; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Author: tzx * @Date: 2023/10/12 15:39 */ @RestController @Tag(name = "登录模块") @RequestMapping(GlobalConstant.SYSTEM_MODULE_PREFIX) @RequiredArgsConstructor public class LoginController { private final ILoginService loginService; private final IUserClient userClient; private final RedisUtil redisUtil; private KeyCloakConfig keyCloakConfig; private SmsSender smsSender; private XjrSmsConfig smsConfig; private IOauthClient oauthClient; private final ILoginConfigClient loginConfigClient; private SsoService ssoService; @PostMapping(value = {"/login/{ltpasToken}", "/login"}) @Operation(summary = "登录", description = "传入账号:account,密码:password") @XjrLog(value = "账号密码登录成功") public R login(@RequestBody @Valid LoginDto dto, @PathVariable(required = false) String ltpasToken) { if(StrUtil.isNotBlank (ltpasToken)) { String userName = ssoService.auth(ltpasToken); if(StrUtil.isNotBlank(userName)) { dto.setUserName(userName); User user = loginService.getUserInfoByName(userName); dto.setPassword(user.getPassword()); return R.ok(loginService.login(dto)); } else { return R.error("秘钥登录失败"); } } return R.ok(loginService.login(dto)); } @PostMapping("/create-token") @Operation(summary = "创建token", description = "传入账号:account,密码:password") @XjrLog(value = "账号密码登录成功") public R createToken(@RequestBody @Valid CreateTokenDto dto) { return R.ok(loginService.createToken(dto)); } /** * 发送验证码 */ @GetMapping("/captcha") @XjrLog(value = "发送验证码") @Operation(summary = "发送验证码", description = "传入账号:mobile") public R captcha(@RequestParam String mobile) { // 验证短信限时发送次数验证 // 获取该号码短信的发送次数 Object o = redisUtil.get(GlobalConstant.CACHE_COUNT_SMS_CODE_PREFIX + mobile); int sendCount = NumberUtils.toInt(String.valueOf(o)); if (o == null) { // 未发送过验证码,初始化 redisUtil.set(GlobalConstant.CACHE_COUNT_SMS_CODE_PREFIX + mobile, 1, smsConfig.getLimitTime() * 3600); } else if (sendCount > smsConfig.getLimitCount()) { // 发送次数超过限制 return R.error("发送验证码次数达上限,请稍后再试!"); } else { // 更新发送次数 long expire = redisUtil.getExpire(GlobalConstant.CACHE_COUNT_SMS_CODE_PREFIX + mobile); redisUtil.set(GlobalConstant.CACHE_COUNT_SMS_CODE_PREFIX + mobile, sendCount + 1, expire); } //生成六位数的字符串 String code = RandomUtils.nextInt(100000, 999999) + StringPool.EMPTY; smsSender.sendCaptcha(mobile, code); redisUtil.set(GlobalConstant.CAPTCHA + StringPool.UNDERSCORE + mobile, code, 60L); return R.ok(Boolean.TRUE); } /** * 验证码 */ @PostMapping("/captcha") @XjrLog(value = "验证码登录") public R loginByCaptcha(@RequestBody LoginCaptchaDto loginCaptchaDto) { String mobile = loginCaptchaDto.getMobile(); String code = redisUtil.get(GlobalConstant.CAPTCHA + StringPool.UNDERSCORE + mobile); if (code != null && StringUtils.equals(code, loginCaptchaDto.getCode())) { User user = userClient.getUserByMobileFeign(mobile); if (user == null) { return R.error("用户不存在!"); } if (user.getEnabledMark() == EnabledMark.DISABLED.getCode()) { return R.error("账户未启用"); } //此登录接口登录web端 StpUtil.login(user.getId(), "PC"); SaSession tokenSession = StpUtil.getTokenSession(); tokenSession.set(GlobalConstant.LOGIN_USER_INFO_KEY, user); Map vo = new HashMap<>(1); vo.put(GlobalConstant.TOKEN_KEY, StpUtil.getTokenValue()); return R.ok(vo); } return R.error("验证码不存在!"); } /** * 退出 */ @PostMapping("/logout") public R logout() { StpUtil.logout(); return R.ok("登出成功!"); } @PostMapping("/token") @Operation(summary = "根据keycloak-token 登录", description = "传入keycloak-token") @XjrLog(value = "keycloak-token登录成功") public R loginByToken(@RequestBody KeyCloakLoginInfoDto dto) { Map credentialsMap = new HashMap<>(1); credentialsMap.put("secret", keyCloakConfig.getSecret()); Configuration configuration = new Configuration(keyCloakConfig.getUrl(), keyCloakConfig.getRealm(), keyCloakConfig.getClientId(), credentialsMap, null); AuthzClient authzClient = AuthzClient.create(configuration); AccessTokenResponse response = authzClient.obtainAccessToken(keyCloakConfig.getUserName(), keyCloakConfig.getPassword()); if (StrUtil.isNotBlank(response.getError())) { return R.error(response.getError()); } //TODO keycloak 登陆过 解析token获取数据 做框架登录操作 JWT jwt = JWTUtil.parseToken(dto.getToken()); Object code = jwt.getPayload(keyCloakConfig.getPayload()); User user = userClient.getUserByCodeFeign(String.valueOf(code)); List list = loginConfigClient.getAllListFeign(); if (list.size() == 0){//如果没有,则设置为默认配置,并进行保存 LoginConfig loginConfig = new LoginConfig(); loginConfig.setId(1L); loginConfig.setMulLogin("0,1"); loginConfig.setMutualExclusion(YesOrNoEnum.YES.getCode()); loginConfig.setWithoutLogin(YesOrNoEnum.YES.getCode()); loginConfig.setPasswordStrategy(YesOrNoEnum.YES.getCode()); loginConfig.setStrategyMaxNumber(7); list.add(loginConfig); loginConfigClient.addLoginConfigFeign(loginConfig); } LoginConfig loginConfig = list.get(0); if (user == null) { return R.error("帐号密码错误!"); } else if (!Integer.valueOf(1).equals(user.getEnabledMark())) { return R.error("当前账号已被锁定,请联系管理员!"); } if (StrUtil.isNotBlank(dto.getDevice()) && dto.getDevice() == "PC" && !loginConfig.getMulLogin().contains("0")){ throw new MyException("当前Web端登录未授权,请联系管理员!"); }else if (StrUtil.isNotBlank(dto.getDevice()) && dto.getDevice() == "APP" && !loginConfig.getMulLogin().contains("1")){ throw new MyException("当前APP端登录未授权,请联系管理员!"); }else { if (StrUtil.isBlank(loginConfig.getMulLogin()) || !loginConfig.getMulLogin().contains("0")){ throw new MyException("当前Web端登录未授权,请联系管理员!"); } } SaTokenConfig oldConfig = SaManager.getConfig(); if (loginConfig.getMutualExclusion() == YesOrNoEnum.NO.getCode()){ //不开启同端互斥 oldConfig.setIsConcurrent(true); }else { //开启同端互斥 oldConfig.setIsConcurrent(false); } // 注入到 SaManager 中 SaManager.setConfig(oldConfig); if (loginConfig.getWithoutLogin() == YesOrNoEnum.YES.getCode()){//开启就设置为7天免登录 StpUtil.login(user.getId(),new SaLoginModel().setDevice(dto.getDevice()).setTimeout(60 * 60 * 24 * 7)); }else { //此登录接口登录web端,IsLastingCookie设置为false,也就是关闭浏览器后再次打开需要重新登录 StpUtil.login(user.getId(), new SaLoginModel().setDevice(dto.getDevice()).setIsLastingCookie(false)); } SaSession tokenSession = StpUtil.getTokenSession(); tokenSession.set(GlobalConstant.LOGIN_USER_INFO_KEY, user); Map vo = new HashMap<>(1); vo.put(GlobalConstant.TOKEN_KEY, StpUtil.getTokenValue()); return R.ok("登录成功!", vo); } @PostMapping("/qrcode-login") @Operation(summary = "oauth 扫码登录", description = "oauth 扫码登录") @XjrLog(value = "oauth 扫码登录") public R createAuthorizeUrl(@Valid @RequestBody CreateAuthorizeUrlDto dto){ AuthRequest authRequest = oauthClient.getAuthRequest(dto.getSource()); String authorizeUrl = authRequest.authorize(AuthStateUtils.createState()); return R.ok(authorizeUrl); } }