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