Springには、…ContextHolderというクラスがある。スレッドローカルに値を保存しておくことで、情報をいろんなところから参照できるようにする。
スレッドローカルは…まあスレッド固有の値ですよね。(あたりまえ)
TomcatなどのAPサーバはリクエストごとにworkerスレッドを割り当てるので、
Java ブロッキングとかノンブロッキングを理解したい - SIerだけど技術やりたいブログ
@AsyncやExecutorServiceを利用して別スレッドでXXXContextHolderを利用しない限り、どこからでもスレッドローカルに登録した同じ値を参照できる。具体的なクラスは、
Spring MVC | RequestContextHolder | リクエスト情報 |
Spring Security | SecurityContextHolder | 認証情報 |
使い方
Spring Bootでリクエストと認証のログを出してみる。spring-boot-starter-securityがclasspathに含まれてるとデフォルトでDIGEST認証がかかるので、今回は認証にそれを使う。
ログ処理はHandlerInterceptorを使う。
引数にHttpServletRequestが取れるけど、わざとRequestContextHolderから取得する。
@Slf4j
public class LoggingInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String msg = (String) request.getParameter("msg");
log.info("request : " + msg);
SecurityContext sc = SecurityContextHolder.getContext();
Authentication authentication = sc.getAuthentication();
log.info("authentication : " + authentication);
return true;
}
}
HandlerInterceptorを登録する。
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor())
.addPathPatterns("/**");
}
}
リクエストするためのメソッドを用意する。
@RestController
@SpringBootApplication
public class ContextholderApplication {
public static void main(String[] args) {
SpringApplication.run(ContextholderApplication.class, args);
}
@GetMapping(value = "sample", params = "msg")
public String param() {
return "success";
}
}
起動するとログに認証パスワードが表示されるので、それを使ってリクエストする。
...
2017-02-18 19:39:47.925 INFO 13184 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-02-18 19:39:47.955 INFO 13184 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-02-18 19:39:48.227 INFO 13184 --- [ main] b.a.s.AuthenticationManagerConfiguration :
Using default security password: e5ad05bf-3daa-4a1b-91ad-81d726ed712a
curl --user user:e5ad05bf-3daa-4a1b-91ad-81d726ed712a http://localhost:8080/sample?msg=hello
ログに出る。
2017-02-18 19:40:01.634 INFO 13184 --- [nio-8080-exec-1] com.example.LoggingInterceptor : request : hello
2017-02-18 19:40:01.634 INFO 13184 --- [nio-8080-exec-1] com.example.LoggingInterceptor : authentication : org.springframework.security.authentication.UsernamePasswordAuthenticationToken@442b5a9f: Principal: org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_USER
うん。
・・・うん。