オーストラリアからこんにちは!
インターン生の古家です。
みなさんはSpringのアプリケーションでIPアドレスの検証がしたい!と思ったことはありますか?
例えば、アプリケーションにIPアドレスによってアクセス制限をかけたいときなどに、IPアドレスの検証が必要になります。
このサイトにあるように、configレベルで静的に制限をかけるのはSpring Securityの機能で簡単にできます。しかし、例えばユーザーごとに別々のIPアドレス制限をかけるなど、動的に制限をかけたい場合はどうでしょう?
実はSpring Securityには、IpAddressMatcherというクラスがあります。これとカスタムのAuthenticationProviderを組み合わせて使えば、いとも簡単に動的なIPアドレス制限ができてしまうのです!!
IpAddressMatcherとは?
IpAddressMatcherとは、その名の通りIPアドレスのマッチングをしてくれるクラスです!
使い方は非常にシンプルです。
まず、許可したいIPアドレスのStringで指定し、それをコンストラクタに渡してnewします。
IpAddressMatcher matcher = new IpAddressMatcher("192.168.1.3");
これだけでMatcherは完成です!なんて簡単なんでしょう。
また、CIDR表記により範囲を指定することも可能です。
matcher = new IpAddressMatcher("192.168.1.3/24");
あとは、そのIpAddressMatcherインスタンスのmatches()メソッドにIPアドレスをStringで渡すだけで、検証を行ってくれます。指定された範囲内のIPであればTrue、そうでなければFalseを返してくれます。
matcher.matches("192.168.1.5") // returns True
matcher.matches("192.168.0.5") // returns False
もう一度いいます。なんて簡単なんでしょう!
オムライスを作るより簡単です。
実際の実装は、以下を参考にしてみてください。
AuthenticationProviderを実装する
validateIpForClient()メソッドで、IpAddressMatcherを用いたIPアドレスの検証を行っています。
@RequiredArgsConstructor public class CustomIpAuthenticationProvider implements AuthenticationProvider { private final UserDetailsService userDetailsService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { WebAuthenticationDetails details = (WebAuthenticationDetails) authentication.getDetails(); String userIp = details.getRemoteAddress(); AuthorizedUser authorizedUser = (AuthorizedUser) userDetailsService.loadUserByUsername(authentication.getName()); try { validateIpForClient(userIp, authorizedUser); } catch (Exception e) { authentication.setAuthenticated(false); throw new BadCredentialsException("Invalid IP Address"); } return new UsernamePasswordAuthenticationToken(authorizedUser, authorizedUser.getPassword(), authorizedUser.getAuthorities()); } static void validateIpForClient(String userIp, AuthorizedUser user) throws AuthenticationException { for (IpAddress address: user.getIpAddresses()) { IpAddressMatcher matcher = new IpAddressMatcher(address.getAddress()); if (matcher.matches(userIp)) { return; } } throw new BadCredentialsException("Invalid IP Address"); } @Override public boolean supports(Class<?> clazz) { return clazz.equals(UsernamePasswordAuthenticationToken.class); } }
SecurityConfigでAuthenticationProviderを使用する
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomIpAuthenticationProvider authenticationProvider; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authenticationProvider); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/login").permitAll() .anyRequest().authenticated() .and().formLogin().permitAll() .and().csrf().disable(); } }
いかがでしたか?なかなか簡単ですよね。
今回は以上です。みなさんも是非つかってみてください。