21

Spring MVC + Spring Security+Hibernateを使用してRESTfulAPIを作成しています。APIは、JSONとHTMLの両方を生成できます。スプリングセキュリティのために適切なエラー処理を行うことは、私に頭痛の種を与えています。

認証はさまざまな方法で行うことができます。BasicAuth、POSTリクエストのさまざまなパラメータ、およびWebログインを介して。認証メカニズムごと<http>に、spring securityxmlconfigのnamespace要素で宣言されたフィルターがあります。

すべての春の例外をカスタムで処理しますHandlerExceptionResolver。これは、コントローラーでスローされたすべての例外に対して正常に機能しますが、カスタムスプリングセキュリティフィルターでスローされたカスタム例外を処理する方法がわかりません。スプリングセキュリティフィルターはコントローラーが呼び出される前に来るので、カスタムスプリングセキュリティフィルターでスローする例外は表示されません。

私はここにstackoverflowでこの質問を見つけました:SpringSecurity でカスタム例外を使用してください。ただし、そこでスローされる例外をどこで処理するのかわかりません。このアプローチを試しましたが、カスタムHandlerExceptionResolverは呼び出されません。代わりに、tomcatによってレンダリングされた醜いスタックトレースがユーザーに表示されます。

なぜ私たちはこれが必要なのですか? ユーザーはアクティブ化および非アクティブ化できます。それらが非アクティブ化され、特定のアクションを実行しようとした場合、カスタムエラーメッセージとともにJSONを返します。これは、スプリングセキュリティが。をスローしたときに表示されるものとは異なる必要がありますAccessDeniedExceptionAccessDeniedExceptionどういうわけか私たちHandlerExceptionResolverにそれをします、しかし私はどれほど正確に続くことができませんでした。

考えられる解決策ExceptionTranslationFilterカスタム例外をスローするときにこれは呼び出されません(doFilter()メソッドのcatchステートメントにブレークポイントを設定します)。私の理解では、このcatchブロックを呼び出し、認証エントリポイントを使用する必要があります。

別の可能性:ExceptionTranslationFilterスプリングセキュリティフィルターチェーンと同様のことを行い、それが行うことと同様のことを行うことができますAccessDeniedHandler

RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);
dispatcher.forward(request, response);

リクエストにいくつかのパラメーター(エラーコード、理由など)を追加し、JSONまたはHTMLでのレンダリングを処理するコントローラーを指すようにすることができます。

これが私たちの構成の短い抜粋です:

春のセキュリティ:

<http create-session="stateless" use-expressions="true" >
    <!-- Try getting the authorization object from the request parameters. -->
    <security:custom-filter ref="filter1" after="SECURITY_CONTEXT_FILTER"/>
    <security:custom-filter ref="filter2" before="LOGOUT_FILTER"/>
    <!-- Intercept certain URLS differently -->

    <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
    <!-- Some more stuff here -->
    <intercept-url pattern="/**" access="denyAll" />  
    <http-basic />
</http>

HandlerExceptionResolverのAppConfig

@Bean
public HandlerExceptionResolver handlerExceptionResolver(){
    logger.info("creating handler exception resolver");
    return new AllExceptionHandler();
}

カスタムHandlerExceptionResolver

public class AllExceptionHandler implements HandlerExceptionResolver {

    private static final Logger logger = LoggerFactory
        .getLogger(AppConfig.class);

    @Override
    public ModelAndView resolveException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) {
    // This is just a snipped of the real method code
    return new ModelAndView("errorPage");
}

フィルタの1つの関連部分:

try {
    Authentication authResult = authenticationManger.authenticate(authRequest);
    SecurityContextHolder.getContext().setAuthentication(authResult);
}

catch(AuthenticationException failed) {
    SecurityContextHolder.clearContext();
    throw failed; 
}

Web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>xxx.xxx.xxx.config</param-value>
</context-param>
<context-param>
    <param-name>spring.profiles.default</param-name>
    <param-value>LIVE</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value></param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <!-- Add multipart support for files up to 10 MB -->
    <multipart-config>
        <max-file-size>10000000</max-file-size>
    </multipart-config>
</servlet>
<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
    <filter-name>openEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>openEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<!-- Map filters -->
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<error-page>
    <error-code>404</error-code>
    <location>/handle/404</location>
</error-page>
</web-app>

誰かがこれをどのように解決できるかについての指針を持っていますか?私はグーグルで多くの記事を調べました、それらのほとんどはフィルターがリクエストを認証することができないときに春のセキュリティによってスローされたAccessDeniedExceptionを処理する方法を説明しています。

SpringSecurity3.1.0とSpringWebMVC3.1.0を使用しています。

4

2 に答える 2

11

SpringSecurityのフィルターの順序が重要であることを覚えておくことが重要です。

Spring Security 3の本から:

ExceptionTranslationFilter、フィルターチェーン実行スタックでその下にスローされた例外のみを処理して対応することができます。ユーザーは、特にカスタムフィルターを間違った順序で追加するときに、予期される動作がアプリケーションの実際の例外処理と異なる理由について混乱することがよくあります。これらの多くの場合、フィルターの順序が原因です。

フィルタが承認に関するものである場合、このアプローチはデフォルトの承認フィルタで使用されるため、これらをチェーンの最後に配置することをお勧めします。そうすれば、車輪の再発明をする必要はありません。

標準フィルター:ドキュメントの表

フィルタチェーンを適切に構成した後、エラーページ、またはカスタムハンドラーを構成できます。詳細については、ドキュメントをご覧ください。

于 2012-11-27T13:44:54.970 に答える
1

ExceptionTranslationFilterは、AuthenticationExceptionとAccessDeniedExceptionの2つの例外のみを処理し、これら2つの例外のカスタムハンドラーを使用します。他の種類の例外や実行時の例外についてはどうでしょうか。

Springフィルタースタックのほぼすべての例外をどのように処理/インターセプトしますか?リクエストをキャッチして取得するSpringの標準的な方法はありませんか(すべての上にカスタムフィルターを作成する以外に)、すべての上に別のフィルターを作成せずに応答しますか?

<security:http auto-config="false" use-expressions="true"
disable-url-rewriting="true" entry-point-ref="authenticationEntryPoint"
pattern="/**">

<security:custom-filter before="FIRST" ref="stackExceptionFilter" />

<security:custom-filter before="..." ref="authenticationFilter" />
<security:logout />
</security:http> 

さて、私は最後に別のフィルターを追加し(またはweb.xmlで/ *のフィルターを構成し)、catchブロックを試行し、キャッチされなかった例外をExceptionControllerメソッドを呼び出すカスタム例外ハンドラーSpringコンポーネントに委任しました(各メソッドはさまざまな方法でさまざまな応答タイプ)フェイルセーフな方法で、例外タイプ(要件)に基づいてカスタム例外メッセージも返します。唯一の欠点は、ループを続けないようにロジックを追加することでした。コントローラのSpringカスタムExceptionHandlerExceptionResolverおよび@ExceptionHandlerは、フィルタ例外を処理せず、例外メッセージを(XML / JSON、リダイレクト、転送など)として返す方法に制限があります。

エラーコードについても同じですが、web.xmlで静的ページを定義しますが、フィルターをERRORディスパッチャーにマッピングし、エラーコードを表示するページのモデルを準備することで静的ページをキャッチします。

于 2013-12-19T04:46:56.753 に答える