Springでフォームベースの認証でより多くのデータを送信することは可能ですか?
-
06-07-2019 - |
質問
Spring Framework およびSpringセキュリティは比較的新しいです。
HTML:カスタム認証スキームを使用しました:
<form action="j_spring_security_check">
<input type="text" name="j_username" value="abc"/>
<input type="text" name="j_password" value="abc"/>
<input type="text" name="myCustom1" value="pqr"/> <!-- maybe type="hidden" -->
<input type="text" name="myCustom2" value="pqr"/> <!-- maybe type="hidden" -->
</form>
および対応するコード:
public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider
{
@Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken)
throws AuthenticationException
{
System.out.println("Method invoked : additionalAuthenticationChecks isAuthenticated ? :"+usernamePasswordAuthenticationToken.isAuthenticated());
}
@Override protected UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException
{
System.out.println("Method invoked : retrieveUser");
//I have Username,password:
//HOW CAN I ACCESS "myCustom1", "myCustom2" here ?
}
}
解決
ユーザー名とパスワードを操作するために追加のフォームパラメーターを使用する必要がある場合は、独自のAuthenticationProcessingFilterを実装できます
このクラスはHttpRequestへの完全なアクセス権を持つため、送信するすべての追加パラメーターにアクセスできます。目標がこれらの値を使用してユーザー名とパスワードを変更することである場合、ここでそれを行います。
他のヒント
上記はすべて、優れた完璧なソリューションです。 しかし、私は完全に問題なく動作する回避策を使用しました。 ThreadLocalにマルチテナントIDを使用
package com.mypackage.servlet;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.util.Assert;
public class ThreadLocalContextUtil implements Filter{
private static final ThreadLocal<Object> contextHolder =
new ThreadLocal<Object>();
public static void setTenantId(Object tenantId) {
Assert.notNull(tenantId, "customerType cannot be null");
contextHolder.set(tenantId);
}
public static Object getTenantId() {
return contextHolder.get();
}
public static void clearTenant() {
contextHolder.remove();
}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// Set the tenant Id into a ThreadLocal object
ThreadLocalContextUtil.setTenantId(request);
if(chain != null)
chain.doFilter(request, response);
else {
//error
}
}
public void init(FilterConfig filterconfig) throws ServletException {
}
}
spring security xml
<security:http auto-config="true" use-expressions="true" access-denied-page="/forms/auth/403" >
<security:custom-filter before="FIRST" ref="tenantFilter" />
......
</security:http>
認証クラスのアクセスリクエストオブジェクト
HttpServletRequest currRequest = (HttpServletRequest) ThreadLocalContextUtil.getTenantId();
次に、リクエストオブジェクトを使用してカスタムパラメータを取得します
ここでのコツは、UsernameAndPasswordAuthenicationTokenを拡張する新しいAuthenicationToken(おそらく)を作成し、@ emillsが言うように、新しいAuthenciationProcessingFilterを実装して要求値をトークンにマッピングし、AuthenicationManagerに送信する必要があることです
基本的に、春のセキュリティでカスタム認証チェーンを実装するためのいくつかの部分があります
-
AuthenicationToken
-認証リクエストとその結果の詳細。つまり、認証に必要な資格情報が含まれています -
AuthenicationProvider
-AuthenicationManagerに登録され、AuthenicationTokenを受け入れてユーザーを検証し、付与された権限が設定されたトークンを返します -
AuthenciationFilter
-AbstractProcessingFilterを使用するだけで実際にフィルターである必要はありません。
次のようにします:
<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
...
<property name="authenticationDetailsSource">
<bean class="org.acegisecurity.ui.AuthenticationDetailsSourceImpl">
<property name="clazz"
value="com.MyAuthenticationDetails"/>
</bean>
</property>
</bean>
これは、プロパティを保持するクラスです:
package com;
import javax.servlet.http.HttpServletRequest;
import org.acegisecurity.ui.WebAuthenticationDetails;
public class MyAuthenticationDetails extends WebAuthenticationDetails {
public MyAuthenticationDetails() {
super();
}
//This constructor will be invoqued by the filter
public MyAuthenticationDetails(HttpServletRequest request) {
super(request);
this.myCustom1 = request.getParameter("myCustom1");
}
public String getMyCustom1() {
return myCustom1;
}
private String myCustom1;
}
これでユーザー名、パスワード、詳細がわかりました。
同様のことをしましたが、ここで誰かが提案したのとは異なります。これが「正しい」と言っているわけではありません。それを行う方法-しかし、それは私にとって非常にうまく機能しています。プリンシパルオブジェクトにはユーザーがあり、AuthenticationTokenにはDetailsオブジェクトもあり、他のログイン情報のMap(String、String)を保存できます。
public class RequestFormDeatils extends SpringSecurityFilter {
protected void doFilterHttp(HttpServletRequest request, ...) {
SecurityContext sec = SecurityContextHolder.getContent();
AbstractAuthenticationToken auth = (AbstractAuthenticationToken)sec.getAuthentication();
Map<String, String> m = new HashMap<String, String>;
m.put("myCustom1", request.getParamtere("myCustom1"));
m.put("myCustom2", request.getParameter("myCustom2"));
auth.setDetails(m);
}
コードの任意の場所でSecurityContextを使用して、このセキュリティ関連情報をUserDetailsオブジェクトに結合したり、引数として渡したりすることなく伝達します。 Spring Security Filterチェーンの最後にあるSecurityFilterでこのコードを実行します。
<bean id="requestFormFilter" class="...RequestFormDetails">
<custom-filter position="LAST" />
</bean>