Skip to content

Commit

Permalink
Merge pull request #6243 from EightMonth/springboot3_sas
Browse files Browse the repository at this point in the history
sas兼容shiro处理
  • Loading branch information
zhangdaiscott authored Jun 20, 2024
2 parents 48555b5 + cddf23c commit e616c5d
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
Expand All @@ -32,13 +33,14 @@ public class JeecgBootExceptionHandler {
*/

@ExceptionHandler(JeecgCaptchaException.class)
@ResponseStatus(HttpStatus.OK)
public Result<?> handleJeecgCaptchaException(JeecgCaptchaException e) {
log.error(e.getMessage(), e);
return Result.error(e.getCode(), e.getMessage());
}

@ExceptionHandler(AuthenticationException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ResponseStatus(HttpStatus.OK)
public Result<?> handleJeecgCaptchaException(AuthenticationException e) {
log.error(e.getMessage(), e);
return Result.error(401, e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
*/
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().build();
return AuthorizationServerSettings.builder().tokenEndpoint("/sys/login").build();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.springframework.util.StringUtils;

import java.security.Principal;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -89,13 +90,20 @@ public Authentication authenticate(Authentication authentication) throws Authent
String captcha = (String) additionalParameter.get("captcha");
String checkKey = (String) additionalParameter.get("checkKey");

OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(appGrantAuthenticationToken);
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();

// 检查登录失败次数
if(isLoginFailOvertimes(username)){
throw new JeecgBootException("该用户登录失败次数过多,请于10分钟后再次登录!");
Map<String, Object> map = new HashMap<>();
map.put("message", "该用户登录失败次数过多,请于10分钟后再次登录!");
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,"fdsafas", Instant.now(), Instant.now().plusNanos(1)), null, map);
}

if(captcha==null){
throw new JeecgBootException("验证码无效");
Map<String, Object> map = new HashMap<>();
map.put("message", "验证码无效");
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,"fdsafas", Instant.now(), Instant.now().plusNanos(1)), null, map);
}
String lowerCaseCaptcha = captcha.toLowerCase();
// 加入密钥作为混淆,避免简单的拼接,被外部利用,用户自定义该密钥即可
Expand All @@ -104,16 +112,15 @@ public Authentication authenticate(Authentication authentication) throws Authent
Object checkCode = redisUtil.get(realKey);
//当进入登录页时,有一定几率出现验证码错误 #1714
if(checkCode==null || !checkCode.toString().equals(lowerCaseCaptcha)) {
log.warn("验证码错误,key= {} , Ui checkCode= {}, Redis checkCode = {}", checkKey, lowerCaseCaptcha, checkCode);
// 改成特殊的code 便于前端判断
throw new JeecgCaptchaException(HttpStatus.PRECONDITION_FAILED.value(), "验证码错误");
Map<String, Object> map = new HashMap<>();
map.put("message", "验证码错误");
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,"fdsafas", Instant.now(), Instant.now().plusNanos(1)), null, map);
}

OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(appGrantAuthenticationToken);
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();

if (!registeredClient.getAuthorizationGrantTypes().contains(authorizationGrantType)) {
throw new JeecgBootException("非法登录");
Map<String, Object> map = new HashMap<>();
map.put("message", "非法登录");
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,"fdsafas", Instant.now(), Instant.now().plusNanos(1)), null, map);
}

// 通过用户名获取用户信息
Expand All @@ -131,7 +138,9 @@ public Authentication authenticate(Authentication authentication) throws Authent
password = PasswordUtil.encrypt(username, password, loginUser.getSalt());
if (!password.equals(loginUser.getPassword())) {
addLoginFailOvertimes(username);
throw new JeecgBootException("用户名或密码不正确");
Map<String, Object> map = new HashMap<>();
map.put("message", "用户名或密码不正确");
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,"fdsafas", Instant.now(), Instant.now().plusNanos(1)), null, map);
}

//由于在上面已验证过用户名、密码,现在构建一个已认证的对象UsernamePasswordAuthenticationToken
Expand All @@ -156,9 +165,9 @@ public Authentication authenticate(Authentication authentication) throws Authent
OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);
if (generatedAccessToken == null) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
"无法生成访问token,请联系管理系。", ERROR_URI);
throw new OAuth2AuthenticationException(error);
Map<String, Object> map = new HashMap<>();
map.put("message", "无法生成访问token,请联系管理系。");
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,"fdsafas", Instant.now(), Instant.now().plusNanos(1)), null, map);
}
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
Expand All @@ -180,9 +189,9 @@ public Authentication authenticate(Authentication authentication) throws Authent
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
"无法生成刷新token,请联系管理员。", ERROR_URI);
throw new OAuth2AuthenticationException(error);
Map<String, Object> map = new HashMap<>();
map.put("message", "无法生成刷新token,请联系管理员。");
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,"fdsafas", Instant.now(), Instant.now().plusNanos(1)), null, map);
}

refreshToken = (OAuth2RefreshToken) generatedRefreshToken;
Expand All @@ -200,7 +209,7 @@ public Authentication authenticate(Authentication authentication) throws Authent
baseCommonService.addLog("用户名: " + username + ",登录成功!", CommonConstant.LOG_TYPE_1, null,loginUser);

JSONObject addition = new JSONObject(new LinkedHashMap<>());

addition.put("token", accessToken.getTokenValue());
// 设置租户
JSONObject jsonObject = commonAPI.setLoginTenant(username);
addition.putAll(jsonObject.getInnerMap());
Expand All @@ -224,8 +233,15 @@ public Authentication authenticate(Authentication authentication) throws Authent
addition.put("multi_depart", 2);
}

// 兼容原有shiro登录结果处理
Map<String, Object> map = new HashMap<>();
map.put("result", addition);
map.put("code", 200);
map.put("success", true);
map.put("timestamp", System.currentTimeMillis());

// 返回access_token、refresh_token以及其它信息给到前端
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, addition);
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, map);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.springframework.util.StringUtils;

import java.security.Principal;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -90,6 +91,9 @@ public Authentication authenticate(Authentication authentication) throws Authent
String captcha = (String) additionalParameter.get("captcha");
String checkKey = (String) additionalParameter.get("checkKey");

OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(passwordGrantAuthenticationToken);
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();

// 检查登录失败次数
if(isLoginFailOvertimes(username)){
throw new JeecgBootException("该用户登录失败次数过多,请于10分钟后再次登录!");
Expand All @@ -105,16 +109,15 @@ public Authentication authenticate(Authentication authentication) throws Authent
Object checkCode = redisUtil.get(realKey);
//当进入登录页时,有一定几率出现验证码错误 #1714
if(checkCode==null || !checkCode.toString().equals(lowerCaseCaptcha)) {
log.warn("验证码错误,key= {} , Ui checkCode= {}, Redis checkCode = {}", checkKey, lowerCaseCaptcha, checkCode);
// 改成特殊的code 便于前端判断
throw new JeecgCaptchaException(HttpStatus.PRECONDITION_FAILED.value(), "验证码错误");
Map<String, Object> map = new HashMap<>();
map.put("message", "验证码错误");
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,"fdsafas", Instant.now(), Instant.now().plusNanos(1)), null, map);
}

OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(passwordGrantAuthenticationToken);
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();

if (!registeredClient.getAuthorizationGrantTypes().contains(authorizationGrantType)) {
throw new JeecgBootException("非法登录");
Map<String, Object> map = new HashMap<>();
map.put("message", "非法登录");
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,"fdsafas", Instant.now(), Instant.now().plusNanos(1)), null, map);
}

// 通过用户名获取用户信息
Expand All @@ -132,7 +135,10 @@ public Authentication authenticate(Authentication authentication) throws Authent
password = PasswordUtil.encrypt(username, password, loginUser.getSalt());
if (!password.equals(loginUser.getPassword())) {
addLoginFailOvertimes(username);
throw new JeecgBootException("用户名或密码不正确");

Map<String, Object> map = new HashMap<>();
map.put("message", "用户名或密码不正确");
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,"fdsafas", Instant.now(), Instant.now().plusNanos(1)), null, map);
}

//由于在上面已验证过用户名、密码,现在构建一个已认证的对象UsernamePasswordAuthenticationToken
Expand All @@ -157,9 +163,9 @@ public Authentication authenticate(Authentication authentication) throws Authent
OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);
if (generatedAccessToken == null) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
"无法生成访问token,请联系管理系。", ERROR_URI);
throw new OAuth2AuthenticationException(error);
Map<String, Object> map = new HashMap<>();
map.put("message", "无法生成访问token,请联系管理系。");
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,"fdsafas", Instant.now(), Instant.now().plusNanos(1)), null, map);
}
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
Expand All @@ -181,9 +187,9 @@ public Authentication authenticate(Authentication authentication) throws Authent
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
"无法生成刷新token,请联系管理员。", ERROR_URI);
throw new OAuth2AuthenticationException(error);
Map<String, Object> map = new HashMap<>();
map.put("message", "无法生成访问token,请联系管理系。");
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,"fdsafas", Instant.now(), Instant.now().plusNanos(1)), null, map);
}

refreshToken = (OAuth2RefreshToken) generatedRefreshToken;
Expand All @@ -201,6 +207,7 @@ public Authentication authenticate(Authentication authentication) throws Authent
baseCommonService.addLog("用户名: " + username + ",登录成功!", CommonConstant.LOG_TYPE_1, null,loginUser);

JSONObject addition = new JSONObject(new LinkedHashMap<>());
addition.put("token", accessToken.getTokenValue());

// 设置租户
JSONObject jsonObject = commonAPI.setLoginTenant(username);
Expand All @@ -225,8 +232,15 @@ public Authentication authenticate(Authentication authentication) throws Authent
addition.put("multi_depart", 2);
}

// 兼容原有shiro登录结果处理
Map<String, Object> map = new HashMap<>();
map.put("result", addition);
map.put("code", 200);
map.put("success", true);
map.put("timestamp", System.currentTimeMillis());

// 返回access_token、refresh_token以及其它信息给到前端
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, addition);
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, map);
}

@Override
Expand Down
Loading

0 comments on commit e616c5d

Please sign in to comment.