オーストラリアからこんにちは!
インターン生の古家です。
みなさんは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 Truematcher.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();
}
}いかがでしたか?なかなか簡単ですよね。
今回は以上です。みなさんも是非つかってみてください。
