私はモデル全体について非常に混乱していると言わざるを得ず、すべてのフローティングピースを接着するのに助けが必要です。
Spring RESTではなく、単なるWebMVCコントローラーを実行しています。
私の使命:ユーザー名と認証に合格したフォームログインが必要です。サードパーティのサービスに対して認証したい。成功したら、Cookieを返しますが、デフォルトのCookieトークンメカニズムは使用しません。代わりに、CookieにJWTトークンが必要です。 Cookieメカニズムを活用することにより、すべてのリクエストがJWTで送信されます。
分解するために、次のモジュールを用意します。
認証が成功したら、Cookieセッショントークンをカスタム実装に置き換えます
リクエストごとにCookieからJWTを解析します(フィルターを使用)
jWTからユーザーの詳細/データを抽出して、コントローラーにアクセスできるようにします
混乱しているのは何ですか? (私が間違っている箇所を修正してください)
サードパーティ認証
サードパーティに対して認証するには、AuthenticationProviderを拡張してカスタムプロバイダーが必要です。
public class JWTTokenAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate( Authentication authentication ) throws AuthenticationException {
// auth against 3rd party
// return Authentication
return new UsernamePasswordAuthenticationToken( name, password, new ArrayList<>() );
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals( UsernamePasswordAuthenticationToken.class );
}
}
質問:
CookieトークンをJWTに置き換えます
これを上品に行う方法はわかりませんが、いくつかの方法を考えることができますが、Spring Securityの方法ではなく、フローから抜け出したくありません。ここでの提案に感謝します!
CookieからのすべてのリクエストでJWTを解析します
私が理解していることから、AbstractAuthenticationProcessingFilterをそのように拡張する必要があります
public class CookieAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
@Override
public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response )
throws AuthenticationException, IOException, ServletException {
String token = "";
// get token from a Cookie
// create an instance to Authentication
TokenAuthentication authentication = new TokenAuthentication(null, null);
return getAuthenticationManager().authenticate(tokenAuthentication);
}
@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
super.doFilter(req, res, chain);
}
}
質問:
Cookieセッションの交換を除いて、必要なものはすべて揃っているようですが、単一の一貫したモデルに入れることはできず、メカニズムを十分に理解している人から必要なため、すべてを単一のモジュールに接着できます。
更新1
OK、私はこれがどこから始まっているのかを得ていると思います... https://github.com/spring-projects/spring-security/blob/master/web/src/main/Java/org/springframework/ security/web/authentication/UsernamePasswordAuthenticationFilter.Java
このフィルターは、自身をPOST-> "/ login"に登録し、UsernamePasswordAuthenticationTokenのインスタンスを作成して、次のフィルターに制御を渡します。
質問は、Cookieセッションが設定される場所です。
更新2
DOSのこのセクションは、私が行方不明だったトップレベルのフローを提供します。これを経験している人は誰でもここを見てください... http://docs.spring.io/spring-security/site/docs/ current/reference/htmlsingle /#tech-intro-authentication
AuthenticationProviderに関するこのセクション... http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#core-services-authentication-manager
更新3-作業ケース、これが最良の方法ですか?
そこで、Spring Securityのドキュメントとそのソースを掘り下げた後、最初のモデルが機能するようになりました。今、これを行うと、複数の方法があることに気付きました。この方法を選ぶ理由と、Denysが以下で提案したことについてのアドバイスはありますか?
以下の作業例...
これを元の投稿で説明した方法で機能させるには、これが起こる必要があります...
カスタムフィルター
public class CookieAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public CookieAuthenticationFilter( RequestMatcher requestMatcher ) {
super( requestMatcher );
setAuthenticationManager( super.getAuthenticationManager() );
}
@Override
public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response )
throws AuthenticationException, IOException, ServletException {
String token = "";
// get token from a Cookie
Cookie[] cookies = request.getCookies();
if( cookies == null || cookies.length < 1 ) {
throw new AuthenticationServiceException( "Invalid Token" );
}
Cookie sessionCookie = null;
for( Cookie cookie : cookies ) {
if( ( "someSessionId" ).equals( cookie.getName() ) ) {
sessionCookie = cookie;
break;
}
}
// TODO: move the cookie validation into a private method
if( sessionCookie == null || StringUtils.isEmpty( sessionCookie.getValue() ) ) {
throw new AuthenticationServiceException( "Invalid Token" );
}
JWTAuthenticationToken jwtAuthentication = new JWTAuthenticationToken( sessionCookie.getValue(), null, null );
return jwtAuthentication;
}
@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
super.doFilter(req, res, chain);
}
}
認証プロバイダー
usernamePasswordAuthenticationFilterによって生成されるUsernamePasswordAuthenticationTokenにプロバイダーを添付します。UsernamePasswordAuthenticationFilterは、「/ login」POSTに自身を添付します。 POST to "/ login"を含むformloginの場合、UsernamePasswordAuthenticationTokenが生成され、プロバイダーがトリガーされます。
@Component
public class ApiAuthenticationProvider implements AuthenticationProvider {
@Autowired
TokenAuthenticationService tokenAuthService;
@Override
public Authentication authenticate( Authentication authentication ) throws AuthenticationException {
String login = authentication.getName();
String password = authentication.getCredentials().toString();
// perform API call to auth against a 3rd party
// get User data
User user = new User();
// create a JWT token
String jwtToken = "some-token-123"
return new JWTAuthenticationToken( jwtToken, user, new ArrayList<>() );
}
@Override
public boolean supports( Class<?> authentication ) {
return authentication.equals( UsernamePasswordAuthenticationToken.class );
}
}
カスタム認証オブジェクト
JWTの場合、スタックに沿って必要なデータを運ぶための独自の認証トークンオブジェクトが必要です。
public class JWTAuthenticationToken extends AbstractAuthenticationToken {
User principal;
String token;
public JWTAuthenticationToken( String token, User principal, Collection<? extends GrantedAuthority> authorities ) {
super( authorities );
this.token = token;
this.principal = principal;
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return principal;
}
public void setToken( String token ) {
this.token = token;
}
public String getToken() {
return token;
}
}
認証成功ハンドラー
これは、カスタムプロバイダーがサードパーティに対してユーザーを認証し、JWTトークンを生成することでその役割を果たしたときに呼び出されます。これは、Cookieが応答に入る場所です。
@Component
public class AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
if( !(authentication instanceof JWTAuthenticationToken) ) {
return;
}
JWTAuthenticationToken jwtAuthenticaton = (JWTAuthenticationToken) authentication;
// Add a session cookie
Cookie sessionCookie = new Cookie( "someSessionId", jwtAuthenticaton.getToken() );
response.addCookie( sessionCookie );
//clearAuthenticationAttributes(request);
// call the original impl
super.onAuthenticationSuccess( request, response, authentication );
}
}
これをまとめてフックする
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired @Required
ApiAuthenticationProvider apiAuthProvider;
@Autowired @Required
AuthenticationSuccessHandler authSuccessHandler;
@Autowired @Required
SimpleUrlAuthenticationFailureHandler authFailureHandler;
@Override
protected void configure( AuthenticationManagerBuilder auth ) throws Exception {
auth.authenticationProvider( apiAuthProvider );
}
@Override
protected void configure( HttpSecurity httpSecurity ) throws Exception {
httpSecurity
// don't create session
.sessionManagement()
.sessionCreationPolicy( SessionCreationPolicy.STATELESS )
.and()
.authorizeRequests()
.antMatchers( "/", "/login", "/register" ).permitAll()
.antMatchers( "/js/**", "/css/**", "/img/**" ).permitAll()
.anyRequest().authenticated()
.and()
// login
.formLogin()
.failureHandler( authFailureHandler )
//.failureUrl( "/login" )
.loginPage("/login")
.successHandler( authSuccessHandler )
.and()
// JWT cookie filter
.addFilterAfter( getCookieAuthenticationFilter(
new AndRequestMatcher( new AntPathRequestMatcher( "/account" ) )
) , UsernamePasswordAuthenticationFilter.class );
}
@Bean
SimpleUrlAuthenticationFailureHandler getAuthFailureHandler() {
SimpleUrlAuthenticationFailureHandler handler = new SimpleUrlAuthenticationFailureHandler( "/login" );
handler.setDefaultFailureUrl( "/login" );
//handler.setUseForward( true );
return handler;
}
CookieAuthenticationFilter getCookieAuthenticationFilter( RequestMatcher requestMatcher ) {
CookieAuthenticationFilter filter = new CookieAuthenticationFilter( requestMatcher );
filter.setAuthenticationFailureHandler( authFailureHandler );
return filter;
}
}
最も簡単な方法は、Spring Sessionをプロジェクトに追加し、 HttpSessionStrategy を拡張することです。これは、セッション作成/破棄イベントに便利なフックを提供し、HttpServletRequestからセッションを抽出するメソッドを備えています。