如何在 Spring MVC-Spring Security 应用程序中处理 GWT RPC 调用的会话过期异常

Posted

技术标签:

【中文标题】如何在 Spring MVC-Spring Security 应用程序中处理 GWT RPC 调用的会话过期异常【英文标题】:How to handle session expired exception in Spring MVC-Spring Security app for GWT RPC calls 【发布时间】:2012-12-11 14:37:30 【问题描述】:

我有 Spring MVC 应用程序,其中安全性由 Spring Security 处理。

UI 是使用 GWT 构建的,它使用 RPC 方法从服务器获取数据。

我需要在 UI 上处理会话过期的情况: 例如 RPC AsyncCallback 可以获取 SessionExpiredException 类型的异常,并弹出窗口显示“您的会话已过期,请点击刷新链接”之类的消息。

有人处理过这样的问题吗?

谢谢。

【问题讨论】:

【参考方案1】:

我想为了处理传入的 GWT 调用,您使用一些 Spring MVC 控制器或一些 servlet。它可以有以下逻辑

try
    // decode payload from  GWT call
    com.google.gwt.user.server.rpc.RPC.decodeRequest(...)
    // get spring bean responsible for actual business logic
    Object bean = applicationContext.getBean(beanName);
    // execute business logic and encode response
    return RPC.invokeAndEncodeResponse(bean, ….)
 catch (com.google.gwt.user.server.rpc.UnexpectedException ex) 
    // send unexpected exception to client
    return RPC.encodeResponseForFailure(..., new MyCustomUnexpectedException(), …) ;

这个案例的解决方案

HttpServletRequest request = getRequest() ; 
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) 
    return RPC.encodeResponseForFailure(..., new MyCustomSessionExpiredException(), …) ;
 else 
    // first code snippet goes here

然后在客户端代码中捕获自定义会话过期异常。如果您不直接使用 RPC,请提供有关 GWT 和 Spring 之间的桥接实现的更多详细信息。

您还需要强制 GWT 编译器将 MyCustomSessionExpiredException 类型包含到序列化白名单中(以防止 GWT 安全策略停止向客户端传播异常的情况)。解决方案:在每个同步接口的每个方法签名中包含 MyCustomSessionExpiredException 类型:

@RemoteServiceRelativePath("productRpcService.rpc")
public interface ProductRpcService extends RemoteService 
    List<Product> getAllProducts() throws ApplicationException;
    void removeProduct(Product product) throws ApplicationException;


MyCustomSessionExpiredException extends ApplicationException

然后在客户端代码中显示弹窗:

public class ApplicationUncaughtExceptionHandler implements GWT.UncaughtExceptionHandler 
    @Override        
    public void onUncaughtException(Throwable caught) 
        if (caught instanceof MyCustomSessionExpiredException) 
            Window.alert("Session expired");
        
    


// Inside of EntryPoint.onModuleLoad method
GWT.setUncaughtExceptionHandler(new ApplicationUncaughtExceptionHandler());

【讨论】:

非常感谢您分享您的知识。您的方法很好,但它将传输层与业务代表混合在一起,并使用不鼓励的内部 GWT API。请在我的答案中查看演示,您可以在 google 代码托管中找到该演示。谢谢。【参考方案2】:

我研究了一下并在http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired%253Fstate%253Dclosed这里上传了解决方案。

签出后使用mvn jetty:run-war查看演示并转到rpc-security-sample/index.htm

有两种方法可以解决

第一个是传递 GWT RemoteServlet 的委托代理,它在方法调用期间抛出 SessionExpiredException。这需要在每个 RPC 服务方法中声明Exception。示例:http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired%253Fstate%253Dclosed

步骤:

    开发先拦截的新过滤器

    为了简单起见,在每个可以继承RuntimeException的RPC方法服务中声明SessionExpiredException(无需在实现者中遵循这一点)

    开发父泛型AsyncCallback处理程序

    使用http://code.google.com/p/gspring/ 解决方案来处理所有传入的RCP 请求。

第二种更简单:返回 401 HTTP 错误并在 UI 端处理(GWT 原生一般异常包含 HTTP 状态号)。示例:http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired-401

第二种方法最简单,不需要在服务方法契约中声明异常。但是,遵循第一种方法可以为您提供一些灵活性:它可能包含一些附加信息,例如上次登录时间(对于 SessionExpiredException)等。第二种方法还可以引入从 SecurityException 继承的新异常,例如列入黑名单的用户(例如如果用户在其会话期间被列入黑名单),或者例如,如果用户像机器人一样经常执行相同的操作(可能会要求它通过验证码)等。

【讨论】:

以上是关于如何在 Spring MVC-Spring Security 应用程序中处理 GWT RPC 调用的会话过期异常的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring Security thymeleaf sec 中使用列表:授权标签

如何使用 Thymeleaf 配置 Spring Boot 并使用 sec:authentication 标签

sec:authentication 在 Spring Boot Security 中不起作用

如何在 Spring Security 的同一个请求中使用两个身份验证提供程序?

sec:authorize 在 Spring 启动应用程序中使用 Thymeleaf 时未按预期工作

<sec:authorize ifAnyGranted 或 ifAnyGranted 在 SPRING SECURITY 中不起作用