<p>原创 梦想de星空 <a id="js_name">macrozheng</a> <em id="publish_time">今天</em></p>
<p>来自专辑</p>
<p>Spring Cloud学习教程</p>
<blockquote>
<p>在<a href="https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247485818&idx=1&sn=042320bf303b75835bbc5f59607fd2e5&scene=21#wechat_redirect">《微服务权限终极解决方案,Spring Cloud Gateway + Oauth2 实现统一认证和鉴权!》</a>一文中我们介绍了Oauth2在微服务中的使用,但是我们没有自定义Oauth2默认的处理结果。有时候我们真的很希望Oauth2中的认证授权能返回我们指定格式的结果,比如登录认证的结果、网关鉴权不通过的结果等等。本文将详细介绍Oauth2中自定义处理结果的方案,希望对大家有所帮助!</p>
</blockquote>
<h2>解决什么问题</h2>
<blockquote>
<p>自定义Oauth2处理结果,主要是为了统一接口返回信息的格式,从下面几个方面着手。</p>
</blockquote>
<ul><li> <p>自定义Oauth2登录认证成功和失败的返回结果;</p> </li><li> <p>JWT令牌过期或者签名不正确,网关认证失败的返回结果;</p> </li><li> <p>携带过期或者签名不正确的JWT令牌访问白名单接口,网关直接认证失败。</p> </li></ul>
<h2>自定义登录认证结果</h2>
<h3>认证成功返回结果</h3>
<ul><li> <p>我们先来看看默认的返回结果,访问Oauth2登录认证接口:http://localhost:9201/auth/oauth/token</p> </li></ul>
<p><img alt="" src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-77325cc2128ca828b27567cd25046bf5"></p>
<ul><li> <p>我们之前使用的都是统一的通用返回结果<code>CommonResult</code>,Oauth2的这个结果显然不符合,需要统一下,通用返回结果格式如下;</p> </li></ul>
<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>
<ul><li> <p>其实我们只要找到一个关键类就可以自定义Oauth2的登录认证接口了,它就是<code>org.springframework.security.oauth2.provider.endpoint.TokenEndpoint</code>,其中定义了我们非常熟悉的登录认证接口,我们只要自己重写登录认证接口,直接调用默认的实现逻辑,然后把默认返回的结果处理下即可,下面是默认的实现逻辑;</p> </li></ul>
<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)));
}
OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
if (token == null) {
throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
}
return getResponse(token);
}
}
</code></pre>
<ul><li> <p>我们将需要的JWT信息封装成对象,然后放入到我们的通用返回结果的<code>data</code>属性中去;</p> </li></ul>
<pre class="blockcode"><code>/**
* Oauth2获取Token返回信息封装
* Creat |
|