<div>
<p></p>
<div style="text-align:center;">
<img alt="75a99914acd794e5660b59772db57576.png" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-ec5db13d84c67cbc51ea60bc6774d17c.png">
</div>
<blockquote>
在《微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!》一文中我们介绍了Oauth2在微服务中的使用,但是我们没有自定义Oauth2默认的处理结果。有时候我们真的很希望Oauth2中的认证授权能返回我们指定格式的结果,比如登录认证的结果、网关鉴权不通过的结果等等。本文将详细介绍Oauth2中自定义处理结果的方案,希望对大家有所帮助!
</blockquote>
<p>SpringCloud实战电商项目mall-swarm(5.1k+star)地址:https://github.com/macrozheng/mall-swarm</p>
<h2>解决什么问题</h2>
<blockquote>
自定义Oauth2处理结果,主要是为了统一接口返回信息的格式,从下面几个方面着手。
</blockquote>
<ul><li>自定义Oauth2登录认证成功和失败的返回结果;</li><li>JWT令牌过期或者签名不正确,网关认证失败的返回结果;</li><li>携带过期或者签名不正确的JWT令牌访问白名单接口,网关直接认证失败。</li></ul>
<h2>自定义登录认证结果</h2>
<h3>认证成功返回结果</h3>
<ul><li>我们先来看看默认的返回结果,访问Oauth2登录认证接口:http://localhost:9201/auth/oauth/token</li></ul>
<p></p>
<div style="text-align:center;">
<img alt="56f74da25f989489d4368df5fa2cfd72.png" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-3c34ee413b7969b582f53504bf8df6af.png">
</div>
<ul><li>我们之前使用的都是统一的通用返回结果<code>CommonResult</code>,Oauth2的这个结果显然不符合,需要统一下,通用返回结果格式如下;</li></ul>
<div class="blockcode">
<pre class="blockcode"><code>/**
* 通用返回对象
* Created by macro on 2019/4/19.
*/
public class CommonResult<T> {<!-- -->
private long code;
private String message;
private T data;
}</code></pre>
</div>
<ul><li>其实我们只要找到一个关键类就可以自定义Oauth2的登录认证接口了,它就是<code>org.springframework.security.oauth2.provider.endpoint.TokenEndpoint</code>,其中定义了我们非常熟悉的登录认证接口,我们只要自己重写登录认证接口,直接调用默认的实现逻辑,然后把默认返回的结果处理下即可,下面是默认的实现逻辑;</li></ul>
<div class="blockcode">
<pre class="blockcode"><code>@FrameworkEndpoint
public class TokenEndpoint extends AbstractEndpoint {<!-- -->
@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {<!-- -->
if (!(principal instanceof Authentication)) {<!-- -->
throw new InsufficientAuthenticationException(
"There is no client authentication. Try adding an appropriate authentication filter.");
}
String clientId = getClientId(principal);
ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
if (clientId != null && !clientId.equals("")) {<!-- -->
// Only validate the client details if a client authenticated during this
// request.
if (!clientId.equals(tokenRequest.getClientId())) {<!-- -->
// double check to make sure that the client ID in the token request is the same as that in the
// authenticated client
throw new InvalidClientException("Given client ID does not match authenticated client");
}
}
if (authenticatedClient != null) {<!-- -->
oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
}
if (!StringUtils.hasText(tokenRequest.getGrantType())) {<!-- -->
throw new InvalidRequestException("Missing grant type");
}
if (tokenRequest.getGrantType().equals("implicit")) {<!-- -->
throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
}
if (isAuthCodeRequest(parameters)) {<!-- -->
// The scope was requested or determined during the authorization step
if (!tokenRequest.getScope().isEmpty()) {<!-- -->
logger.debug("Clearing scope of incoming token request");
tokenRequest.setScope(Collections.<String> emptySet());
}
}
if (isRefreshTokenRequest(parameters)) {<!-- -->
// A refresh token has its own default scopes, so we should ignore any added by the factory here.
tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
}
OAuth2Acc |
|