2

Spring セキュリティを使用してサーバーを保護し、テストを試みました。私のテストには、テスト ユーザー (DB に既に存在する) のログインが含まれており、ユーザーが認証され、特定の役割を持つ必要がある保護された URL にアクセスしようとしました。

ここに私のコードがあります、

セキュリティ コンテキストを作成しました。

<!-- URL's that start with the "app/secured" prefix requires authentication -->
<http auto-config="false" use-expressions="true">
    <form-login login-processing-url="/app/login"
        authentication-success-handler-ref="ajaxAuthenticationSuccessHandler"
        authentication-failure-handler-ref="ajaxAuthenticationFailureHandler" />
    <intercept-url pattern="/**" access="permitAll" />
    <intercept-url pattern="**/app/secured/**" access="isAuthenticated()" />
</http>

<authentication-manager alias="authenticationManager">
    <authentication-provider user-service-ref="myUserDetailsService">
        <password-encoder ref="passwordEncoder">
            <salt-source user-property="creationTime" />
        </password-encoder>
    </authentication-provider>
</authentication-manager>

<beans:bean id="passwordEncoder"
    class="me.co.server.bl.security.ExtendedShaPasswordEncoder" />

<beans:bean id="ajaxAuthenticationSuccessHandler"
    class="me.co.server.web.resource.register.login.AjaxAuthenticationSuccessHandler" />

<beans:bean id="ajaxAuthenticationFailureHandler"
    class="me.co.server.web.resource.register.login.AjaxAuthenticationFailureHandler" />

<beans:bean id="myUserDetailsService"
    class="me.co.server.bl.security.MyUserDetailsService" />

web.xml の先頭に security-context とセキュリティ フィルターを追加しました。

<!-- Spring Security -->
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
    /WEB-INF/spring/applicationContext.xml
    /WEB-INF/spring/security-context.xml
    </param-value>
</context-param>

これが私のWebレイヤージャージの実装です。

@Path("/secured/{resourceName}")
@Component
public class SecuredResourceProvider extends ServiceResourceProvider {

    /*--- Members ---*/

    private ILogger logger = LogManager.getLogger(SecuredResourceProvider.class);

    @Inject
    protected SecuredResourceFactory securedResourceFactory;

    /*--- Constructors ---*/

    protected SecuredResourceProvider() {
    super("Secured");
    }

    /*--- Public Methods ---*/

    @GET
    @Produces("application/json")
    @Path("/{resourceId}")
    public String getSecuredResource(@PathParam("resourceId") String resourceId, @PathParam("resourceName") String resourceName)
        throws UnhandledResourceException, UnauthorizedAccessException, ServerInternalErrorException, JsonException, ResourceArgumentException {

    // NOT IMPLEMENTED AT THE MOMENT //

    return null;
    }

    @PUT
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/{code}")
    public String putSecuredResource(String resourceData, @PathParam("code") String ownerResourceId, @PathParam("resourceName") String resourceName)
        throws UnhandledResourceException, UnauthorizedAccessException, ServerInternalErrorException, JsonException, ResourceArgumentException {

    if (SecuredResources.isSecuredResource(resourceName)) {
        IResource<String> resource = securedResourceFactory.getResourceInstance(resourceName);

        String resourceString = resource.put(ownerResourceId, resourceData);
        return createReturnResourceString(resourceString);
    } else {
        throw new UnhandledResourceException("Invoking a secured method for an unsecured resource " + resourceName);
    }
    }


    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/{resourceId}")
    public String postSecuredResource(String resourceData, @PathParam("resourceName") String resourceName, @PathParam("resourceId") String resourceId)
        throws UnhandledResourceException, UnauthorizedAccessException, ServerInternalErrorException, JsonException, ResourceArgumentException {

    if (SecuredResources.isSecuredResource(resourceName)) {
        IResource<String> resource = securedResourceFactory.getResourceInstance(resourceName);
        String resourceString = resource.post(resourceId, resourceData);
        return createReturnResourceString(resourceString);
    } else {
        throw new UnhandledResourceException("Invoking a secured method for an unsecured resource " + resourceName);
    }
    }


    @DELETE
    @Consumes(MediaType.TEXT_PLAIN)
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/{code}")
    public String deleteSecuredResource(String resourceId, @PathParam("code") String ownerResourceId, @PathParam("resourceName") String resourceName)
        throws UnhandledResourceException, UnauthorizedAccessException, ServerInternalErrorException, JsonException, ResourceArgumentException {

    if (SecuredResources.isSecuredResource(resourceName)) {
        IResource<String> resource = securedResourceFactory.getResourceInstance(resourceName);

        String resourceString = resource.delete(ownerResourceId, resourceId);
        return createReturnResourceString(resourceString);
    } else {
        throw new UnhandledResourceException("Invoking a secured method for an unsecured resource " + resourceName);
    }
    }
}

私のURLのインターセプト定義に従って、このSpring Beanが保護されている(認証されたユーザーが必要)ことになっていること以外に、ここで特別なことは何もありません。Web レイヤーは、メソッド セキュリティ アノテーションを使用して保護されているビジネス ロジック レイヤーにリクエストを委任します。

    @Override
    @Secured("ROLE_BRAND_MANAGER")
    public String post(String gymCode, String trainingSessionJson) throws UnhandledResourceException, ServerInternalErrorException,
        ResourceArgumentException, JsonException {

...

}

これを機能させるために、 global-method-security 宣言をapplication-contextに追加しました。

<security:global-method-security secured-annotations="enabled" proxy-target-class="true"/>

発行されたユーザーは (当局の賢明な限り) ブランド マネージャーですが、現時点では、スーソリティーは保持されず、エンティティからハードコードされて返されます。

@Entity
@Table(name = "brand_managers")
public class BrandManager implements Serializable, UserDetails {

    /** Serial version unique id */
    private static final long serialVersionUID = -7992146584570782015L;

    public static final String ROLE = "ROLE_BRAND_MANAGER";

    /*--- Members ---*/

    /** The unique, internal ID of the entity. */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private long id;

    /**
     * The creation time of this user
     */
    @Column(name = "creation_time")
    protected long creationTime;

    /**
     * The hashed password
     */
    @Column(name = "password")
    protected String password;

    @Column(name = "first_name")
    protected String firstName;

    @Column(name = "last_name")
    protected String lastName;

    @Column(name = "email")
    protected String eMail;

    @Column(name = "address1")
    protected String address1;

    @Column(name = "address2", nullable = true)
    protected String address2;

    @Column(name = "city")
    protected String city;

    @Column(name = "state")
    protected String state;

    @Column(name = "zip", nullable = true)
    protected String zip;

    @Column(name = "country")
    protected String country;

    @Column(name = "phone")
    protected String phone;

    @Column(name = "brand_id")
    protected int brandId;

    /*--- Constructors ---*/

    /**
     * default
     */
    public BrandManager() {
    setCreationTime(Calendar.getInstance().getTimeInMillis());
    }

    public BrandManager(String password, String firstName, String lastName, String eMail, String address1, String address2, String city,
        String state, String zip, String country, String phone, int brandId) {
    this();
    this.password = password;
    this.firstName = firstName;
    this.lastName = lastName;
    this.eMail = eMail;
    this.address1 = address1;
    this.address2 = address2;
    this.city = city;
    this.state = state;
    this.zip = zip;
    this.country = country;
    this.phone = phone;
    this.brandId = brandId;
    }

    /*--- Overridden Methods ---*/

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
    // currently not holding authorities in DB, but returning hard-coded
    return AuthorityUtils.createAuthorityList(ROLE);
    }
... getter and setters...

}

私の UserDetailsS​​ervice は、単純に DB からユーザーをロードします (dao 委任を使用)。

現在、サーバー (Tomcat で実行) をテストしていて、保護された URL を呼び出していると、奇妙なことがわかります。Spring セキュリティの DecisionManager のデフォルト実装である AffirmativeBased クラスの「決定」メソッドにブレークポイントを配置しました。

パブリック void 決定 (認証認証、オブジェクト オブジェクト、コレクション configAttributes) は AccessDeniedException をスローします { int 拒否 = 0;

for (AccessDecisionVoter voter : getDecisionVoters()) {
    int result = voter.vote(authentication, object, configAttributes);

    if (logger.isDebugEnabled()) {
        logger.debug("Voter: " + voter + ", returned: " + result);
    }

    switch (result) {
    case AccessDecisionVoter.ACCESS_GRANTED:
        return;

    case AccessDecisionVoter.ACCESS_DENIED:
        deny++;

        break;

    default:
        break;
    }
}

@Secured アノテーションが付けられたサービス メソッドを呼び出すと、「configAttributes」プロパティに [permitAll] の値を持つ 1 つの要素が含まれていることがわかりますが、これは期待していたものではありません。「ROLE_BRAND_MANAGER」が表示されることを期待していました。間違った場所をデバッグしていますか? セキュリティ関連のコードが正しいことをどのように確認できますか?

前もってありがとう、ヨギ

4

2 に答える 2

1

AccessDecisionManagerこのような名前空間構成には、実際には 2 つのインスタンスがあります。1 つは Web セキュリティに使用され、もう 1 つはメソッド セキュリティに使用されます。必要に応じて、両方をオーバーライドできます。

ブレークポイントはおそらく、Web セキュリティのブレークポイントが表示されていることを意味します (これは、デバッガーでスタックを調べれば明らかです)。

global-method-securityが、保護する Bean と同じコンテキスト ファイルにあることを確認してください。詳細については、Spring Security FAQ を参照してください。また、親/子スプリング コンテキスト ファイルに関する SO に関する他のディスカッションも見つけることができます。通常、ディスパッチャー サーブレット構成ファイル (例: spring-servlet.xml) で宣言されている MVC コントローラーまたはその他の Web Bean を保護する場合は、そこにも配置する必要があります。ContextLoaderListenerこれらはメイン アプリケーション コンテキスト ( によってロードされたもの) からは見えないweb.xmlため、そこにメソッド セキュリティ要素を配置しても機能しません。

于 2012-08-03T23:35:40.947 に答える
0

php-coderによって提案されたように:

最初のルールが常に優先されるため、intercept-url-sを並べ替えて、permitAllを最後に移動することをお勧めします。

URLインターセプト定義の順序を切り替える必要がありましたが、フローは理にかなっています。

于 2012-08-06T06:58:33.830 に答える