私が取り組んでいるアプリケーションには、フォームベース認証を処理するためのSpringSecurityがすでに備わっています。ここでの要件は、外部サービスを介してプログラムでユーザーにログインすることですifトークンが要求パラメーターの1つに見つかりました。
つまり、「トークン」などの特定のリクエストパラメータが存在する場合、そのトークンを使用して外部サービスを呼び出し、それが有効なトークンかどうかを確認する必要があります。その場合、ユーザーはログインします。
ログインフォームがないため、Spring Securityを「トリガー」または「フックオン」してこのパラメーターをチェックし、検証を行ってから、必要に応じてユーザーを認証する方法と場所がわかりません。これを行うために拡張またはカスタマイズできる何かがSpring Securityにあるはずだと思いましたか?
Spring Securityのドキュメントを調べて、AbstractPreAuthenticatedProcessingFilterが最初から正しいものかどうかを知りました
アプリケーションにも同様の設定があります。私が知る限り、これは基本的な要素です:
次のようにAuthenticationProvider
を作成する必要があります。
public class TokenAuthenticationProvider implements AuthenticationProvider {
@Autowired private SomeService userSvc;
@Override
public Authentication authenticate(Authentication auth) throws AuthenticationException {
if (auth.isAuthenticated())
return auth;
String token = auth.getCredentials().toString();
User user = userSvc.validateApiAuthenticationToken(token);
if (user != null) {
auth = new PreAuthenticatedAuthenticationToken(user, token);
auth.setAuthenticated(true);
logger.debug("Token authentication. Token: " + token + "; user: " + user.getDisplayName());
} else
throw new BadCredentialsException("Invalid token " + token);
return auth;
}
}
また、カスタムパラメータを認証トークンに変換するには、Filter
を作成する必要があります。
public class AuthenticationTokenFilter implements Filter {
@Override
public void init(FilterConfig fc) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain fc) throws IOException, ServletException {
SecurityContext context = SecurityContextHolder.getContext();
if (context.getAuthentication() != null && context.getAuthentication().isAuthenticated()) {
// do nothing
} else {
Map<String,String[]> params = req.getParameterMap();
if (!params.isEmpty() && params.containsKey("auth_token")) {
String token = params.get("auth_token")[0];
if (token != null) {
Authentication auth = new TokenAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
}
fc.doFilter(req, res);
}
@Override
public void destroy() {
}
class TokenAuthentication implements Authentication {
private String token;
private TokenAuthentication(String token) {
this.token = token;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return new ArrayList<GrantedAuthority>(0);
}
@Override
public Object getCredentials() {
return token;
}
@Override
public Object getDetails() {
return null;
}
@Override
public Object getPrincipal() {
return null;
}
@Override
public boolean isAuthenticated() {
return false;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
@Override
public String getName() {
// your custom logic here
}
}
}
これらのBeanを作成する必要があります。
<beans:bean id="authTokenFilter" class="com.example.security.AuthenticationTokenFilter" scope="singleton" />
<beans:bean id="tokenAuthProvider" class="com.example.security.TokenAuthenticationProvider" />
最後に、これらのBeanをセキュリティ構成にワイヤリングする必要があります(それに応じて調整します)。
<sec:http >
<!-- other configs here -->
<sec:custom-filter ref="authTokenFilter" after="BASIC_AUTH_FILTER" /> <!-- or other appropriate filter -->
</sec:http>
<sec:authentication-manager>
<!-- other configs here -->
<sec:authentication-provider ref="tokenAuthProvider" />
</sec:authentication-manager>
別の方法があるかもしれませんが、これは間違いなく機能します(現時点ではSpring Security 3.1を使用しています)。
ターゲット要求パラメーターが渡されるSpringMVCコントローラーまたはサービスを使用する場合は、@ PreAuthorizeSpringセキュリティアノテーションを使用できます。
たとえば、渡されたトークンをチェックして、渡されたトークンが有効な場合に認証を実行できるSpringサービスがあるとします。
@Service("authenticator")
class Authenticator {
...
public boolean checkTokenAndAuthenticate(Object token) {
...
//check token and if it is invalid return "false"
...
//if token is valid then perform programmatically authentication and return "true"
}
...
}
次に、Spring security @PreAuthorizeアノテーションを使用すると、次の方法でこれを行うことができます。
...
@PreAuthorize("@authenticator.checkTokenAndAuthenticate(#token)")
public Object methodToBeChecked(Object token) { ... }
...
また、Springセキュリティアノテーションを有効にし、Spring-security-aspectsをPOM(またはjarからクラスパス)に追加する必要があります。