10

Spring Security の servletApi() サポートは素晴らしいです。

カスタムプリンシパルを次のように挿入したい:

public interface UserPrincipal extends Principal {
   public Integer getId();
}

@RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(UserPrincipal user){
   // implementation
}  

or


@RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(UserPrincipalImpl user){
   // implementation
}

PrincipalSpring は、 の助けを借りてインスタンスの注入をサポートしていますServletRequestMethodArgumentResolver

次のようにプリンシパルを注入しています。

else if (Principal.class.isAssignableFrom(paramType)) {
    return request.getUserPrincipal();
}

ここから問題が始まります。requestのインスタンスですSecurityContextHolderAwareRequestWrapper。次の実装があります。

@Override
public Principal getUserPrincipal() {
    Authentication auth = getAuthentication();

    if ((auth == null) || (auth.getPrincipal() == null)) {
        return null;
    }

    return auth;
 }

Authenticationも であるからPrincipalです。(Spring Security で唯一気に入らなかった部分。これも別の質問にします。)

これが問題を引き起こしています。Authenticationは ではPrincipalないからですUserPrincipal

この問題を解決するにはどうすればよいですか? UserPrincipal である認証も実装する必要がありますか? または、HandlerMethodArgumentResolver オーダーを変更して、カスタム リゾルバーを作成する必要がありますか? (Spring MVC では、内部ハンドラーの優先度が高いため、これは簡単ではありません。)

追加情報として:

私はSpring Security M2を使用しており、私の構成AuthenticationManagerBuilderは単純です:

@Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception        {

  auth
     .userDetailsService(detailsService);
}

何か助けはありますか?

4

2 に答える 2

21

基本的に、これは Spring MVC との統合の問題であり、Spring Security の問題ではないようです。API がオブジェクトを返すため、Spring Security は Authentication@getPrinicpal() がプリンシパルを実装していることを知る方法がありません。

いくつかの選択肢があります。それぞれに長所と短所がありますが、私は@ModelAttribute@ControllerAdvice

@ModelAttribute@ControllerAdvice

最も簡単なオプションは、@ModelAttributeon customでメソッドに注釈を付けること@ControllerAdviceです。詳細については、Spring Referenceを参照してください。

@ControllerAdvice
public class SecurityControllerAdvice {

    @ModelAttribute
    public UserPrincipal customPrincipal(Authentication a) {
        return (UserPrincipal) a == null ? null : a.getPrincipal();
    }
}

これで、コントローラーで次のようなことができます。

@RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(@ModelAttribute UserPrincipal user){
   // implementation
}

が HttpServletRequest#getPrincipal() で@ModelAttribute確実に使用されるようにするためにのみ必要であることに注意してください。@ModelAttributeプリンシパルを実装していない場合@ModelAttributeは不要です。

@Valueおよび ExpressionValueMethodArgumentResolver

次のようなこともできます。

@RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(
  @Value("#{request.userPrincipal.principal}") UserPrincipal user){
   // implementation
}

これが機能するのは、 HttpServletRequest が ExpressionValueMethodArgumentResolver (Spring MVC によってデフォルトで追加された) の属性として使用可能であり、SpEL経由でアクセスできるためです。これは、注釈に含ま@ModelAttributeれている必要がある定数のためよりも魅力的ではないと思います。SPR-10760が解決され、独自のカスタム注釈を次のように使用できるよう@Valueになると、より良い結果が得られます。

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Value("#{request.userPrincipal.principal}")
public @interface CurrentUser { }

@Autowire RequestMappingHandlerAdapter

RequestMappingHandlerAdapter が既に初期化されているため、これは少しずさんですが、次に示すように HandlerMethodArgumentResolvers の順序を変更できます。

@EnableWebMvc
@Configuration
public class WebMvcConfiguration 
  extends WebMvcConfigurerAdapter {
    ...
    @Autowired
    public void setArgumentResolvers(RequestMappingHandlerAdapter adapter) {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
        resolvers.add(new CustomPrincipalArgumentResolver());
        resolvers.addAll(adapter.getArgumentResolvers().getResolvers());
        adapter.setArgumentResolvers(resolvers);
    }
}

サブクラス WebMvcConfigurationSupport

HandlerMethodArgumentResolver が最初に使用されるようにするために、使用する代わりに WebMvcConfigurationSupport を拡張することもでき@EnableWebMvcます。例えば:

@Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {
    ...

    @Bean
    @Override
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
        RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter()();
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
        resolvers.add(new CustomPrincipalArgumentResolver());
        resolvers.addAll(adapter.getArgumentResolvers().getResolvers());
        adapter.setArgumentResolvers(resolvers);
        return adapter;
    }
}
于 2013-07-19T16:44:34.007 に答える