您的位置:首页 > 娱乐 > 明星 > 云主机免费版_上海软件系统开发公司_长尾关键词挖掘词工具_凡科建站官网

云主机免费版_上海软件系统开发公司_长尾关键词挖掘词工具_凡科建站官网

2025/5/2 10:04:45 来源:https://blog.csdn.net/weixin_43356507/article/details/144216327  浏览:    关键词:云主机免费版_上海软件系统开发公司_长尾关键词挖掘词工具_凡科建站官网
云主机免费版_上海软件系统开发公司_长尾关键词挖掘词工具_凡科建站官网

本章内容概览

  1. Spring Security提供的/logout登出接口做了什么与如何自定义。
  2. Spring Authorization Server提供的/connect/logout登出接口做了什么与如何自定义。
  3. Spring Authorization Server提供的/oauth2/revoke撤销token接口做了什么与如何自定义。

前言

  既然系统中有登录功能,那么必然也会有登出的需要,登出的效果一般都是清除认证相关的Session,同时也要让access token无效化,换而言之就是清除当前用户认证相关的状态,使用户在项目中回到未认证状态,因为表单登录以后存储在Session中的状态和请求携带access token访问后端时都代表着用户的认证信息,有了这些认证信息才代表着用户登录过,可以继续接下来的操作。

本章基本上是理论与实验性的内容,编码内容很少,内容可能也会比较多,请大家耐心观看。

Spring Security提供的/logout

访问方式

浏览器直接访问http://127.0.0.1:8080/logout(地址栏直接输入该地址后按回车)

其中127.0.0.1是认证服务所在服务器的ip,8080是认证服务对外提供服务的端口。

分析

  该接口是Security提供的一个接口,该接口是过滤器LogoutFilter拦截处理的,在过滤器内分别调用了LogoutHandlerLogoutSuccessHandler来处理登出的逻辑和登出成功的逻辑,跟踪源码可知具体处理登出的是SecurityContextLogoutHandler,在该类中根据当前请求获取Http Session并销毁,之后清除SecurityContextRepository中保存的认证信息,之后就是登出成功的处理了,处理登出成功的是SimpleUrlLogoutSuccessHandler,该类中会默认重定向至defaultTargetUrl属性的值(应该为url),默认情况下启动时在加载LogoutFilter时会初始化一个SimpleUrlLogoutSuccessHandler的实例给登出过滤器使用,默认会设置defaultTargetUrl属性的值为/login?logout;大致逻辑就是这样。

自定义

  根据上边的分析可知有两个很明显的扩展点,一个是登出处理,一个是登录成功处理,大部分情况下只需要自定义登录成功处理即可,那么该如何配置?请看下方示例代码

在认证相关的过滤器中添加如下代码

// 自定义登出配置
http.logout(logout -> logout// 定义登出路径.logoutUrl("/logout")// 登出时同步撤销Token.addLogoutHandler(new RevokeAccessTokenLogoutHandler(serverProperties))// 自定义登出成功响应处理.logoutSuccessHandler(new JsonLogoutSuccessHandler())
);

RevokeAccessTokenLogoutHandler的具体实现

package com.example.authorization.handler;import com.example.model.Result;
import com.example.util.JsonUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.http.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;import java.nio.charset.StandardCharsets;/*** 登出处理-撤销token** @author vains*/
@Slf4j
@RequiredArgsConstructor
public class RevokeAccessTokenLogoutHandler implements LogoutHandler {private final ServerProperties serverProperties;private final AuthorizationServerSettings authorizationServerSettings;private final RestTemplate restTemplate = new RestTemplate();@Override@SneakyThrowspublic void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {// 获取token(可以是Access Token,也可以是Refresh Token)String token = request.getParameter(OAuth2ParameterNames.TOKEN);if (ObjectUtils.isEmpty(token)) {// 写回json数据Result<String> result = Result.error("token is empty");response.setCharacterEncoding(StandardCharsets.UTF_8.name());response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.getWriter().write(JsonUtils.objectCovertToJson(result));response.getWriter().flush();response.getWriter().close();return;}// 撤销token逻辑Integer port = serverProperties.getPort();// 撤销token地址String revokeUrl = "http://127.0.0.1:"+ (port == null ? "8080" : port)// 默认是/oauth2/revoke+ authorizationServerSettings.getTokenRevocationEndpoint();MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();formData.add(OAuth2ParameterNames.TOKEN, token);// 准备请求的headerHttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);headers.add(HttpHeaders.AUTHORIZATION, "Basic " + HttpHeaders.encodeBasicAuth("messaging-client", "123456", StandardCharsets.UTF_8));// 封装请求HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(formData, headers);// 撤销token,默认无响应try {ResponseEntity<String> responseEntity = restTemplate.postForEntity(revokeUrl, entity, String.class);if (responseEntity.getStatusCode().is2xxSuccessful()) {log.info("revoked access token");}} catch (Exception e) {log.warn("revoked access token failed, cause: {}", e.getMessage());// 撤销token失败写回json数据Result<String> result = Result.error("revoked access token failed, cause: " + e.getMessage());response.setCharacterEncoding(StandardCharsets.UTF_8.name());response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.getWriter().write(JsonUtils.objectCovertToJson(result));response.getWriter().flush();response.getWriter().close();}}}

JsonLogoutSuccessHandler的具体实现,可以在该类中响应json或自己实现重定向逻辑

package com.example.authorization.handler;import com.example.model.Result;
import com.example.util.JsonUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;import java.io.IOException;
import java.nio.charset.StandardCharsets;/*** 登出成功自定义处理** @author vains*/
@Slf4j
public class JsonLogoutSuccessHandler implements LogoutSuccessHandler {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {log.info("onLogoutSuccessHandler");// 写回json数据Result<String> result = Result.success();response.setCharacterEncoding(StandardCharsets.UTF_8.name());response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.getWriter().write(JsonUtils.objectCovertToJson(result));response.getWriter().flush();// 或重定向至某个url// 重定向操作...略}}

这样就可以在销毁当前Session的情况下撤销token了,如图

在这里插入图片描述

未带token参数示例,如图

在这里插入图片描述

现在就完成了销毁Session与撤销token的步骤。

Spring Authorization Server提供的/connect/logout

该接口是Openid Connect 1.0 协议中规定的一个接口。

访问方式

浏览器直接访问http://127.0.0.1:8080/connect/logout?id_token_hint={id_token}&post_logout_redirect_uri={客户端信息中配置的post_logout_redirect_uri}(地址栏直接输入该地址后按回车)

参数说明:

参数描述描述
id_token_hintOAuth2授权登录以后获取的id token协议中是推荐传入,但是在Authorization Server中必填
post_logout_redirect_uri客户端信息中配置的post_logout_redirect_uri登出后重定向的地址,不传会走默认登出处理

更多参数说明详见:spec openid-connect-rpinitiated-1_0 RPLogout

其中127.0.0.1是认证服务所在服务器的ip,8080是认证服务对外提供服务的端口。

分析

  该接口是Spring Authorization Server基于Openid Connect 1.0协议提供的一个OIDC登出接口,接口由OidcLogoutEndpointFilter拦截处理,在拦截器中先调用OidcLogoutAuthenticationConverter获取请求入参并转成OidcLogoutAuthenticationToken返回,之后过滤器再调用AuthenticationManager(ProviderManager)来做登出处理,根据OidcLogoutAuthenticationToken最终由OidcLogoutAuthenticationProvider处理具体的登出逻辑,在该provider中没有针对Session或者access token做什么操作,先根据id_token_hint参数查询oauth2登录生成的认证信息,之后针对认证信息与Session做一些校验,限制了必须是登录时的Session才可以调用登出;之后就是登出成功处理了,上述所说的登出后跳转路径以及销毁当前Session都是在这个登出成功处理中操作的,如果当前Session存在并且获取到表单登录生成的认证信息则调用SecurityContextLogoutHandler处理,这个和上边的/logout登出使用的LogoutHandler一样,逻辑是一样的,都是销毁当前Session与认证信息,接下来是跳转的操作,如果请求/connect/logout时携带了post_logout_redirect_uri参数,则会跳转到该url,如果没有携带该参数,则会通过SimpleUrlLogoutSuccessHandler跳转到根目录。

自定义

  通过上述流程可以发现这个是与/oauth2/token/oauth2/authorize类似的一个接口,或者说Spring Authorization Server提供的内置端点都是这个流程,支持自定义的地方基本就是ConverterProvider、成功响应和失败响应处理,详见文档oidc-logout-endpoint

在端点的过滤器链配置中添加如下配置

http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)// 开启OpenID Connect 1.0协议相关端点.oidc(oidc -> oidc.logoutEndpoint(logoutEndpoint -> logoutEndpoint.logoutResponseHandler((request, response, authorization) -> {})))

具体的实现就不写了,大家知道入口以后根据自己的业务实现登出即可。

注意:如果要使用这个接口,并自定义logoutResponseHandler逻辑请注销Session,因为默认的ConverterProvider都没有注销当前的Session

注意:如果要使用这个接口,并自定义logoutResponseHandler逻辑请注销Session,因为默认的ConverterProvider都没有注销当前的Session

注意:如果要使用这个接口,并自定义logoutResponseHandler逻辑请注销Session,因为默认的ConverterProvider都没有注销当前的Session

如果想在Provider中注销Session也可以,也可以在里边撤销token(撤销token代码请参考上边的RevokeAccessTokenLogoutHandler),提供了很多配置入口,自由度也是比较高的。

不带post_logout_redirect_uri参数访问效果,会清除Session并跳转到根目录/,没有放行直接跳转到登录页面了(代表着认证信息与Session都被清除了)

在这里插入图片描述

post_logout_redirect_uri参数访问效果,会清除Session并跳转到post_logout_redirect_uri参数的值(一个url)。

在这里插入图片描述

请注意,这个url必须要在客户端信息配置中存在,否则会提示错误!

请注意,这个url必须要在客户端信息配置中存在,否则会提示错误!

请注意,这个url必须要在客户端信息配置中存在,否则会提示错误!

在这里插入图片描述

如果集成了十九章的内容,将客户端信息存储在redis,则改代码后重启即可,如果是存储在数据库的请在重启后检查数据库中的数据是否被修改。

Spring Authorization Server提供的/oauth2/revoke

该接口是基于RFC 7009实现的一个接口。

访问方式

curl --location --globoff --request POST 'http://127.0.0.1:8080/oauth2/revoke?token={access token或者refresh token}' \
--header 'Authorization: Basic bWVzc2FnaW5nLWNsaWVudDoxMjM0NTY='

token参数是想撤销的token(access token\refresh token)

其中127.0.0.1是认证服务所在服务器的ip,8080是认证服务对外提供服务的端口。

访问时记得添加basic认证请求头,usernamepassword分别代表客户端的id和密钥
在这里插入图片描述

更多参数及参数的详细说明见RFC 7009 Revocation Request

分析

  该接口是Spring Authorization Server基于OAuth2.1协议提供的一个token撤销接口,接口由OAuth2TokenRevocationEndpointFilter拦截处理,在过滤器内的逻辑与上边提到的OidcLogoutEndpointFilter过滤器的大致流程一致,流程是:通过Converter转换请求参数,然后由Provider将token的状态改为无效状态,最后是处理成功的handler;根据源码可知是OAuth2TokenRevocationAuthenticationConverter来将撤销请求的参数转为OAuth2TokenRevocationAuthenticationToken,然后ProviderManager将生成的OAuth2TokenRevocationAuthenticationToken交由OAuth2TokenRevocationAuthenticationProvider处理,在该provider内将token的状态改为无效状态,默认的成功处理只设置了http 200的状态码,无任何响应。

  这个接口在讲上边的登出接口时都有提过,上边两个接口主要目的是清除认证状态销毁当前Session,但是oauth2登录生成access token/refresh token还可以继续使用,所以需要调用当前接口来将token无效化。

自定义

  跟上边的OidcLogoutEndpointFilter过滤器一样,可以自定义ConverterProvider、成功响应和失败响应处理,详见文档oauth2-token-revocation-endpoint

为避免贴大量代码我这边就只自定义一个成功响应处理,在端点的过滤器链配置中添加如下代码

http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)// 开启OpenID Connect 1.0协议相关端点.oidc(Customizer.withDefaults())// 自定义token撤销断点配置入口.tokenRevocationEndpoint(tokenRevocationEndpoint -> tokenRevocationEndpoint// 撤销成功响应.revocationResponseHandler(new TokenRevocationSuccessHandler()));

TokenRevocationSuccessHandler实现如下,成功后响应json

package com.example.authorization.handler;import com.example.model.Result;
import com.example.util.JsonUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;import java.io.IOException;
import java.nio.charset.StandardCharsets;/*** token撤销成功处理** @author vains*/
@Slf4j
public class TokenRevocationSuccessHandler implements AuthenticationSuccessHandler {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {log.info("Token revocation success");// 写回json数据Result<String> result = Result.success();response.setCharacterEncoding(StandardCharsets.UTF_8.name());response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.getWriter().write(JsonUtils.objectCovertToJson(result));response.getWriter().flush();}
}

携带access token访问测试

在这里插入图片描述

前后端分离中想用axios请求/logout/connect/logout

  这种方式仅适用于使用Spring Session将前后端的Session一同管理,前后端共享Session以后才能使用这种方式,并且初始化axios时需要添加withCredentials: true配置,使其在跨域请求时带上cookie,这样后端才能获取到登录时的Session并进行销毁。(未测试,应该可行)

  如果没有使用Spring Session将前后端的Session一同管理,推荐使用文中的方式,打开一个新标签页并访问登出接口,因为在上边的分析中都讲过是获取当前Session并销毁,如果使用axios访问那获取不到登录时的Session则会跳过销毁Session的步骤。

Jwt token为什么撤销了以后还能继续使用?

  因为jwt是无状态的,资源服务器解析时不会从认证服务查询token的状态,所以在解析时不能知道jwt token的状态,如果想在撤销token以后使jwt token失效可以在redis中添加一个黑名单,解析时查看jwt类型的access token是否在黑名单中,以防止撤销后的token继续使用。

最后

  到此就说明了两个用户登出和一个token撤销的接口,里边的逻辑有些可能没有讲到,但是处理请求的类,以及具体逻辑的处理,相关的类我基本上都贴出来,大家可以熟悉一下源码,然后看看怎么结合自己业务来实现登出相关的内容。

附录

新仓库地址

spec openid-connect-rpinitiated-1_0 RPLogout

oidc-logout-endpoint

RFC 7009

RFC 7009 Revocation Request

oauth2-token-revocation-endpoint

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com