package com.xjrsoft.module.system.controller; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.hutool.jwt.JWT; import cn.hutool.jwt.JWTUtil; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.xjrsoft.common.annotation.XjrLog; import com.xjrsoft.common.constant.GlobalConstant; import com.xjrsoft.common.model.result.R; import com.xjrsoft.common.model.result.RT; import com.xjrsoft.common.sms.SmsCtcc; import com.xjrsoft.common.utils.QrCodeUtil; import com.xjrsoft.common.utils.RedisUtil; import com.xjrsoft.config.CommonPropertiesConfig; import com.xjrsoft.config.KeyCloakConfig; import com.xjrsoft.module.organization.entity.User; import com.xjrsoft.module.organization.service.IUserService; import com.xjrsoft.module.student.dto.QRLoginDto; import com.xjrsoft.module.system.dto.CaptchaDto; import com.xjrsoft.module.system.dto.CreateAuthorizeUrlDto; import com.xjrsoft.module.system.dto.CreateTokenDto; import com.xjrsoft.module.system.dto.KeyCloakLoginInfoDto; import com.xjrsoft.module.system.dto.LoginByCodeDto; import com.xjrsoft.module.system.dto.LoginCaptchaDto; import com.xjrsoft.module.system.dto.LoginDto; import com.xjrsoft.module.system.dto.LoginQRCodeDto; import com.xjrsoft.module.system.service.ILoginService; import com.xjrsoft.module.system.service.IOauthService; import com.xjrsoft.module.system.vo.LoginCheckQRCodeVo; import com.xjrsoft.module.system.vo.LoginQRCodeVo; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.AllArgsConstructor; import me.zhyd.oauth.request.AuthRequest; import me.zhyd.oauth.utils.AuthStateUtils; import org.apache.commons.lang3.RandomUtils; import org.keycloak.authorization.client.AuthzClient; import org.keycloak.authorization.client.Configuration; import org.keycloak.representations.AccessTokenResponse; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; import java.util.HashMap; import java.util.Map; /** *

* 登录控制器 *

* * @author tzx * @since 2022-03-02 */ @Api(tags = "登录模块") @RestController @RequestMapping(GlobalConstant.SYSTEM_MODULE_PREFIX) @AllArgsConstructor public class LoginController { private final ILoginService loginService; private final IUserService userService; private final RedisUtil redisUtil; private KeyCloakConfig keyCloakConfig; private final SmsCtcc smsCtcc; private IOauthService oauthService; private final CommonPropertiesConfig commonPropertiesConfig; @PostMapping("/login") @ApiOperation(value = "登录", notes = "传入账号:account,密码:password") @XjrLog(value = "账号密码登录成功") public R login(@RequestBody @Valid LoginDto dto) throws Exception { return R.ok(loginService.login(dto)); } @PostMapping("/findUserByCode") @ApiOperation(value = "登录", notes = "code") @XjrLog(value = "Code登录成功") public R findUserByCode(@RequestBody @Valid LoginByCodeDto dto) throws Exception { return R.ok(loginService.findUserByCode(dto)); } @PostMapping("/loginByCode") @ApiOperation(value = "登录", notes = "code") @XjrLog(value = "Code登录成功") public R loginByCode(@RequestBody @Valid LoginByCodeDto dto) throws Exception { return R.ok(loginService.loginByCode(dto)); } @GetMapping(value = "/loginQRCode") @ApiOperation(value="登录-二维码") @SaCheckPermission("login:detail") public RT qrcode() { long loginCode = IdUtil.getSnowflakeNextId(); String url = commonPropertiesConfig.getDomainApp() + "pages/login/qrCodeLogin/index?loginCode="+loginCode; String active = SpringUtil.getActiveProfile(); if(!"prod".equals(active)){ url = "http://yxh-web.ngrok.yingcaibx.com/app/#/pages/login/qrCodeLogin/index?loginCode="+loginCode; } int width = 200; int height = 200; int margin = 1; try { String base64 = QrCodeUtil.createBase64(url, width, height, margin); LoginQRCodeVo loginQRCodeVo = new LoginQRCodeVo(); loginQRCodeVo.setImgBase64(base64); redisUtil.set(loginCode + "time", System.currentTimeMillis()); loginQRCodeVo.setLoginCode(loginCode + ""); return RT.ok(loginQRCodeVo); } catch (Exception e) { return RT.error(e.getMessage()); } } @PostMapping("/QR-code-login") @ApiOperation(value = "二维码登录", notes = "code") @XjrLog(value = "二维码登录") public RT loginQRCode(@RequestBody LoginQRCodeDto dto) throws Exception { // Long timestamp = redisUtil.get(dto.getLoginCode() + "time", Long.class); // long timeMillis = System.currentTimeMillis(); // if(timeMillis - timestamp > 300000){ // return RT.error("二维码失效,请刷新重试"); // } Boolean b = loginService.loginQRCode(dto); if(b){ return RT.ok("登录成功"); } return RT.ok("登录失败,未能绑定微信公众号"); } @PostMapping("/check-QR-code-login") @ApiOperation(value = "验证是否登录成功", notes = "验证是否登录成功") @XjrLog(value = "验证是否登录成功") public RT checkLoginQRCode(@RequestBody @Valid QRLoginDto dto) { Long timestamp = redisUtil.get(dto.getLoginCode() + "time", Long.class); if(timestamp == null){ timestamp = System.currentTimeMillis(); } long timeMillis = System.currentTimeMillis(); LoginCheckQRCodeVo loginCheckQRCodeVo = new LoginCheckQRCodeVo(); if(timeMillis - timestamp > 300000){ loginCheckQRCodeVo.setStatus(1); return RT.ok(loginCheckQRCodeVo); } String token = redisUtil.get(dto.getLoginCode()); loginCheckQRCodeVo.setToken(token); loginCheckQRCodeVo.setStatus(0); return RT.ok(loginCheckQRCodeVo); } @PostMapping("/bindOpenid") @ApiOperation(value = "登录", notes = "登录") @XjrLog(value = "code换Openid并绑定") public R bindOpenid(@RequestBody @Valid LoginByCodeDto dto) throws Exception { return R.ok(loginService.bindOpenid(dto)); } @GetMapping("/imgcaptcha") @ApiOperation(value = "图形验证码", notes = "图形验证码") public R imgCaptcha() { return R.ok(loginService.imgCaptcha()); } @PostMapping("/create-token") @ApiOperation(value = "创建token", notes = "传入账号:account,密码:password") @XjrLog(value = "账号密码登录成功") public R createToken(@RequestBody @Valid CreateTokenDto dto) { return R.ok(loginService.createToken(dto)); } /** * 发送验证码 */ @PostMapping("/captcha") @XjrLog(value = "发送验证码") @ApiOperation(value = "发送验证码", notes = "传入账号:mobile") public R captcha(@RequestBody @Valid CaptchaDto captchaDto) { String active = SpringUtil.getActiveProfile(); String code = "111111"; // 测试环境使用模拟短信,正式环境才发送短信 if (active.equals("prod")) { // String captchaCode = redisUtil.get(captchaDto.getKey(), 0); // if (captchaCode == null) { // throw new MyException("验证码已过期,请刷新验证码!"); // } // // if (!captchaCode.equals(captchaDto.getCode())) { // throw new MyException("验证码不正确,请刷新验证码!"); // } //生成六位数的字符串 code = RandomUtils.nextInt(100000, 999999) + StringPool.EMPTY; smsCtcc.sendCaptcha(captchaDto.getMobile(), code, true); } else { smsCtcc.sendCaptcha(captchaDto.getMobile(), code, false); } return R.ok(Boolean.TRUE); } /** * 验证码登录 */ @PostMapping("/loginCaptcha") @XjrLog(value = "验证码登录") public R loginByCaptcha(@RequestBody LoginCaptchaDto loginCaptchaDto) throws Exception { // 验证验证码 if (!smsCtcc.captchaVerify(loginCaptchaDto.getMobile(), loginCaptchaDto.getCode())) { return R.error("验证码不正确!"); } return R.ok(loginService.loginByCaptcha(loginCaptchaDto)); } /** * 退出 */ @PostMapping("/logout") public R logout() { StpUtil.logout(); return R.ok("登出成功!"); } @PostMapping("/token") @ApiOperation(value = "根据keycloak-token 登录", notes = "传入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 = userService.getOne(Wrappers.lambdaQuery(User.class).eq(User::getCode, code)); if (user == null) { return R.error("帐号密码错误!"); } else if (!Integer.valueOf(1).equals(user.getEnabledMark())) { return R.error("该账号已被禁用!"); } //此登录接口登录web端 StpUtil.login(user.getId(), dto.getDevice()); 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") @ApiOperation(value = "oauth 扫码登录", notes = "oauth 扫码登录") @XjrLog(value = "oauth 扫码登录") public R createAuthorizeUrl(@Valid @RequestBody CreateAuthorizeUrlDto dto){ AuthRequest authRequest = oauthService.getAuthRequest(dto.getSource()); String authorizeUrl = authRequest.authorize(AuthStateUtils.createState()); return R.ok(authorizeUrl); } }