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...
}
私の UserDetailsService は、単純に 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」が表示されることを期待していました。間違った場所をデバッグしていますか? セキュリティ関連のコードが正しいことをどのように確認できますか?
前もってありがとう、ヨギ