spring bootで認証機能を実装することは日常茶飯事です。
しかし、Basic認証にもかかわらず、認証成功後の処理、認証失敗後のlock処理や失敗回数リセット処理またはlog出力などを行うこともあるかもしれません。
一般的なformLoginやoauth2Loginであれば、
successHandlerやfailureHandlerに実装すれば解決なんですが。。。
日本語の情報が少なかったため投稿します。
dependency
spring boot 2.0.4.RELEASE
lombok
を使用しています。
実装
security の設定でBasic認証を実装します。
また、SharedObjectでRememberMeServicesインターフェースを実装したclassを登録します。
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
MyRememberMeServices myRememberMeServices;
@Override
protected void configure(HttpSecurity http) throws Exception {
//ここでRemembeMeServiseを入れ替えることでBasic認証の前後で処理を実行できる。
http.setSharedObject(RememberMeServices.class,myRememberMeServices);
http.httpBasic()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); //Cookieによるセッション管理は不要;
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance()) //spring5からは必須?
.withUser("LLENN")
.password("p-chan")
.roles("GGO_USER");
}
}
RememberMeServicesインターフェースにはSuccessまたはFailの際の処理を実装できます。
@Slf4j
@Service
public class MyRememberMeServices implements RememberMeServices {
@Override
public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
return null;
}
@Override
public void loginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
log.info("login:{}",successfulAuthentication.getName());
}
@Override
public void loginFail(HttpServletRequest request, HttpServletResponse response) {
//認証に使われたユーザーを取得する。
String base64Credentials = request.getHeader("authorization").substring("Basic".length()).trim();
String credentialSting = new String(Base64.getDecoder().decode(base64Credentials), Charset.forName("UTF-8"));
String username = credentialSting.split(":")[0];
log.error("login fail:{}",username);
}
}
上記実装ではlogを出力しているだけですが、クラスは@Service
なので好きなcomponentをDIできます。
しかし、loginSuccessとloginFailはBasic認証処理の後に呼ばれていることに気をつけてください。
ログインに失敗した場合は認証情報はありません。
そのため、loginFialの際には認証に使われたユーザー名をAuthentication::getName
からは取得することができず、HttpServletRequestのheader情報から取得する必要があります。
以上です。
ソースはgithubに上げました。
参考にしていただけると幸いです。
https://github.com/amanoese/spring-basic-auth-example
余談
Basic認証のRememberMeServicesの処理を入れ替えるのは良いけど、元の実装ですごい処理してたらどうしよう。。と思ってソースを見たら下記のようなコードでした。
package org.springframework.security.web.authentication;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
public class NullRememberMeServices implements RememberMeServices {
public NullRememberMeServices() {
}
public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
return null;
}
public void loginFail(HttpServletRequest request, HttpServletResponse response) {
}
public void loginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
}
}
書き換えるために用意されているクラスみたいですね。
自分が実装を追った限りautoLoginが呼ばれている場所が見当たらなかったため困惑したりしました。。。
springは難しい。