206 lines
9.7 KiB
Java
206 lines
9.7 KiB
Java
package com.njcn.auth.controller;
|
||
|
||
import cn.hutool.core.collection.CollectionUtil;
|
||
import cn.hutool.json.JSONObject;
|
||
import com.nimbusds.jose.jwk.JWKSet;
|
||
import com.nimbusds.jose.jwk.RSAKey;
|
||
import com.njcn.auth.service.UserTokenService;
|
||
import com.njcn.auth.utils.AuthPubUtil;
|
||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||
import com.njcn.common.pojo.constant.OperateType;
|
||
import com.njcn.common.pojo.constant.SecurityConstants;
|
||
import com.njcn.common.pojo.dto.UserTokenInfo;
|
||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||
import com.njcn.common.pojo.exception.BusinessException;
|
||
import com.njcn.common.pojo.response.HttpResult;
|
||
import com.njcn.common.utils.HttpResultUtil;
|
||
import com.njcn.common.utils.LogUtil;
|
||
import com.njcn.common.utils.sm.DesUtils;
|
||
import com.njcn.redis.utils.RedisUtil;
|
||
import com.njcn.user.api.PassWordRuleFeugnClient;
|
||
import com.njcn.user.api.UserFeignClient;
|
||
import com.njcn.user.enums.UserResponseEnum;
|
||
import com.njcn.user.pojo.po.UserStrategy;
|
||
import com.njcn.web.controller.BaseController;
|
||
import com.njcn.web.utils.RequestUtil;
|
||
import com.njcn.web.utils.RestTemplateUtil;
|
||
import io.swagger.annotations.Api;
|
||
import io.swagger.annotations.ApiImplicitParam;
|
||
import io.swagger.annotations.ApiImplicitParams;
|
||
import io.swagger.annotations.ApiOperation;
|
||
import lombok.AllArgsConstructor;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.apache.commons.collections.CollectionUtils;
|
||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
|
||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||
import org.springframework.web.bind.annotation.*;
|
||
import org.springframework.web.util.UriComponentsBuilder;
|
||
import springfox.documentation.annotations.ApiIgnore;
|
||
|
||
import java.net.URI;
|
||
import java.security.KeyPair;
|
||
import java.security.Principal;
|
||
import java.security.interfaces.RSAPublicKey;
|
||
import java.time.LocalDateTime;
|
||
import java.time.ZoneOffset;
|
||
import java.util.*;
|
||
import java.util.stream.Collectors;
|
||
|
||
/**
|
||
* @author hongawen
|
||
*/
|
||
@Api(tags = "认证中心")
|
||
@Slf4j
|
||
@RestController
|
||
@RequestMapping("/oauth")
|
||
@AllArgsConstructor
|
||
public class AuthController extends BaseController {
|
||
|
||
|
||
private final TokenEndpoint tokenEndpoint;
|
||
|
||
private final KeyPair keyPair;
|
||
|
||
private final RedisUtil redisUtil;
|
||
|
||
private final UserFeignClient userFeignClient;
|
||
|
||
private final PassWordRuleFeugnClient passWordRuleFeugnClient;
|
||
|
||
private final UserTokenService userTokenService;
|
||
|
||
|
||
@ApiIgnore
|
||
@OperateInfo(info = LogEnum.SYSTEM_SERIOUS, operateType = OperateType.AUTHENTICATE)
|
||
@ApiOperation("登录认证")
|
||
@ApiImplicitParams({
|
||
@ApiImplicitParam(name = SecurityConstants.GRANT_TYPE, defaultValue = "password", value = "授权模式", required = true),
|
||
@ApiImplicitParam(name = SecurityConstants.CLIENT_ID, defaultValue = "njcn", value = "Oauth2客户端ID", required = true),
|
||
@ApiImplicitParam(name = SecurityConstants.CLIENT_SECRET, defaultValue = "njcnpqs", value = "Oauth2客户端秘钥", required = true),
|
||
@ApiImplicitParam(name = SecurityConstants.REFRESH_TOKEN, value = "刷新token"),
|
||
@ApiImplicitParam(name = SecurityConstants.USERNAME, value = "登录用户名"),
|
||
@ApiImplicitParam(name = SecurityConstants.PASSWORD, value = "登录密码"),
|
||
@ApiImplicitParam(name = SecurityConstants.IMAGE_CODE, value = "图形验证码"),
|
||
@ApiImplicitParam(name = SecurityConstants.PHONE, value = "手机号"),
|
||
@ApiImplicitParam(name = SecurityConstants.SMS_CODE, value = "短信验证码"),
|
||
})
|
||
@PostMapping("/token")
|
||
public Object postAccessToken(@ApiIgnore Principal principal, @RequestParam @ApiIgnore Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
|
||
String methodDescribe = getMethodDescribe("postAccessToken");
|
||
String username = parameters.get(SecurityConstants.USERNAME);
|
||
|
||
|
||
String grantType = parameters.get(SecurityConstants.GRANT_TYPE);
|
||
if (grantType.equalsIgnoreCase(SecurityConstants.GRANT_CAPTCHA) || grantType.equalsIgnoreCase(SecurityConstants.REFRESH_TOKEN_KEY)) {
|
||
username = DesUtils.aesDecrypt(username);
|
||
} else if (grantType.equalsIgnoreCase(SecurityConstants.GRANT_SMS_CODE)) {
|
||
//短信方式登录,将手机号赋值为用户名
|
||
username = parameters.get(SecurityConstants.PHONE);
|
||
}
|
||
|
||
//判断当前登录是否超过最大并发数
|
||
UserStrategy data = passWordRuleFeugnClient.getUserStrategy().getData();
|
||
String onlineUserKey = SecurityConstants.TOKEN_ONLINE_PREFIX;
|
||
List<UserTokenInfo> onLineUser = (List<UserTokenInfo>) redisUtil.getLikeListAllValues(onlineUserKey);
|
||
if(CollectionUtil.isNotEmpty(onLineUser)){
|
||
String finalUsername = username;
|
||
onLineUser = onLineUser.stream().filter(item->{
|
||
JSONObject jsonObject = AuthPubUtil.getLoginByToken(item.getRefreshToken());
|
||
String login = jsonObject.getStr(SecurityConstants.USER_NAME_KEY);
|
||
long exp = Long.parseLong(jsonObject.getStr(SecurityConstants.JWT_EXP));
|
||
long now = Calendar.getInstance().getTimeInMillis()/1000;
|
||
return (exp > now) && !login.equals(finalUsername);
|
||
}).collect(Collectors.toList());
|
||
}
|
||
|
||
Integer maxNum = data.getMaxNum();
|
||
if((CollectionUtil.isNotEmpty(onLineUser)?onLineUser.size():0)>=maxNum){
|
||
throw new BusinessException(UserResponseEnum.LOGIN_USER_OVERLIMIT);
|
||
}
|
||
|
||
|
||
if (grantType.equalsIgnoreCase(SecurityConstants.REFRESH_TOKEN_KEY)) {
|
||
//如果是刷新token,需要去黑名单校验
|
||
userTokenService.judgeRefreshToken(parameters.get(SecurityConstants.REFRESH_TOKEN_KEY));
|
||
}
|
||
RequestUtil.saveLoginName(username);
|
||
OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
|
||
//用户的登录名&密码校验成功后,判断当前该用户是否可以正常使用系统
|
||
if (!grantType.equalsIgnoreCase(SecurityConstants.GRANT_SMS_CODE)) {
|
||
userFeignClient.judgeUserStatus(username);
|
||
}
|
||
//登录成功后,记录token信息,并处理踢人效果
|
||
userTokenService.recordUserInfo(oAuth2AccessToken, RequestUtil.getRealIp());
|
||
if (!grantType.equalsIgnoreCase(SecurityConstants.PASSWORD)) {
|
||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, oAuth2AccessToken, methodDescribe);
|
||
} else {
|
||
return oAuth2AccessToken;
|
||
}
|
||
}
|
||
|
||
@OperateInfo(info = LogEnum.SYSTEM_SERIOUS, operateType = OperateType.LOGOUT)
|
||
@ApiOperation("用户登出系统")
|
||
@DeleteMapping("/logout")
|
||
public HttpResult<Object> logout() {
|
||
String methodDescribe = getMethodDescribe("logout");
|
||
String userIndex = RequestUtil.getUserIndex();
|
||
String username = RequestUtil.getUsername();
|
||
LogUtil.njcnDebug(log, "{},用户名为:{}", methodDescribe, username);
|
||
String blackUserKey = SecurityConstants.TOKEN_BLACKLIST_PREFIX + userIndex;
|
||
String onlineUserKey = SecurityConstants.TOKEN_ONLINE_PREFIX + userIndex;
|
||
Object onlineTokenInfoOld = redisUtil.getObjectByKey(onlineUserKey);
|
||
List<UserTokenInfo> blackUsers = (List<UserTokenInfo>) redisUtil.getObjectByKey(blackUserKey);
|
||
UserTokenInfo userTokenInfo;
|
||
if (!Objects.isNull(onlineTokenInfoOld)) {
|
||
//清除在线token信息
|
||
redisUtil.delete(onlineUserKey);
|
||
userTokenInfo = (UserTokenInfo) onlineTokenInfoOld;
|
||
if (CollectionUtils.isEmpty(blackUsers)) {
|
||
blackUsers = new ArrayList<>();
|
||
}
|
||
blackUsers.add(userTokenInfo);
|
||
LocalDateTime refreshTokenExpire = userTokenInfo.getRefreshTokenExpire();
|
||
long lifeTime = Math.abs(refreshTokenExpire.plusMinutes(5L).toEpochSecond(ZoneOffset.of("+8")) - LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));
|
||
redisUtil.saveByKeyWithExpire(blackUserKey, blackUsers, lifeTime);
|
||
}
|
||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
|
||
}
|
||
|
||
|
||
/**
|
||
* 文档隐藏该接口
|
||
*/
|
||
@ApiIgnore
|
||
@ApiOperation("RSA公钥获取接口")
|
||
@GetMapping("/getPublicKey")
|
||
public Map<String, Object> getPublicKey() {
|
||
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
||
RSAKey key = new RSAKey.Builder(publicKey).build();
|
||
return new JWKSet(key).toJSONObject();
|
||
}
|
||
|
||
/**
|
||
* 自动登录
|
||
*/
|
||
@OperateInfo(info = LogEnum.SYSTEM_COMMON, operateType = OperateType.AUTHENTICATE)
|
||
@ApiOperation("自动登录")
|
||
@PostMapping("/autoLogin")
|
||
@ApiImplicitParam(name = "phone", value = "手机号", required = true, paramType = "query")
|
||
@ApiIgnore
|
||
public HttpResult<Object> autoLogin(@RequestParam String phone) {
|
||
String methodDescribe = getMethodDescribe("autoLogin");
|
||
String userUrl = "http://127.0.0.1:10214/oauth/token";
|
||
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(userUrl)
|
||
.queryParam("grant_type", "sms_code")
|
||
.queryParam("client_id", "njcnapp")
|
||
.queryParam("client_secret", "njcnpqs")
|
||
.queryParam("phone", phone)
|
||
.queryParam("smsCode", "123456789");
|
||
URI uri = builder.build().encode().toUri();
|
||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, Objects.requireNonNull(RestTemplateUtil.post(uri, HttpResult.class).getBody()).getData(), methodDescribe);
|
||
}
|
||
|
||
}
|