10
11

More than 5 years have passed since last update.

Spring Securityを使って、3つ以上のパラメータをJSON形式で認証する

Last updated at Posted at 2019-02-07

はじめに

Spring Securityでは、デフォルトでForm認証で、パラメータ名がusernamepasswordとなっている。そんなに凝ったことしなくてもいいのであれば、デフォルトの認証機構を使うことで、コード量を減らせるが、最近だとJSON形式による認証も増えてきているので、Spring Securityでどうカスタマイズしたらいいのか色々調べてみた。

認証パラメータ

今回の認証を行うためのパラメータは以下の通り。

パラメータ名 内容
email メールアドレス
password パスワード
tenantCode テナントコード

手順

1. 認証用のパラメータを格納するクラスを用意

Principal.java
@Getter
@Setter
public class Principal {
  private String email;
  private String tenantCode;
  private String password;
}

2. ログインユーザ情報を格納するクラスを用意

LoginUser.java
@Getter
@Setter
public class LoginUser extends org.springframework.security.core.userdetails.User{
  private long userId;
  private String userName;
  // 他に保持したいプロパティがあれば追加
}

3. 認証の前処理を行うフィルターを用意

CustomAuthenticationFilter.java
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

  @Override
  public Authentication attemptAuthentication(HttpServletRequest request,
      HttpServletResponse response) {

    try {
      Principal principal = new ObjectMapper().readValue(request.getInputStream(),
          Principal.class);

      UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
          principal, null);

      setDetails(request, authRequest);

      return this.getAuthenticationManager().authenticate(authRequest);

    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  protected void successfulAuthentication(HttpServletRequest req,
      HttpServletResponse res,
      FilterChain chain,
      Authentication auth) {

    SecurityContextHolder.getContext().setAuthentication(auth);

  }
}

4. 認証処理を行うクラスを用意

CustomAuthenticationProvider.java
@Configuration
public class CustomAuthenticationProvider implements AuthenticationProvider {

  // ユーザ情報リポジトリ
  @Autowired
  UserRepository userRepository;

  // テナント情報リポジトリ
  @Autowired
  TenantRepository tenantRepository;

  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {

    // CustomAuthenticationFilterクラスから渡されたものを取得
    Principal principal = (Principal) authentication.getPrincipal();

    if (principal == null) {
      throw new BadCredentialsException("認証情報がありません");
    }

    // テナントコードの有効チェック
    Tenant tenant = tenantRepository.findByTenantCode(principal.getTenantCode());
    if (tenant == null || tenant.getStatus() == 0) {
      throw new BadCredentialsException("テナントが無効です");
    }

        // メールアドレスとテナントコードでユーザの有効チェック
    User user = userRepository.findByEmailAndTenantId(principal.getEmail(), tenant.getId());

    // ユーザー情報を取得できなかった場合
    if (user == null) {
      throw new BadCredentialsException("ユーザが存在しません");
    }

    if (!new BCryptPasswordEncoder().matches(principal.getPassword(), user.getPassword())) {
      throw new BadCredentialsException("パスワードが間違っています");
    }

    List<GrantedAuthority> authorityList = new ArrayList<>();

    // 権限付与処理

        LoginUser loginUser = new LoginUser();
    loginUser.setUserId(user.getId());
    loginUser.setUserName(user.getUserName());

    return new UsernamePasswordAuthenticationToken(loginUser, principal.getPassword(),
        authorityList);
  }

  @Override
  public boolean supports(Class<?> authentication) {
    return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
  }

}

5. WebSecurityConfigurerAdapterの継承クラスを用意

SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  @Autowired
  CustomAuthenticationProvider authenticationProvider;

  @Override
  protected void configure(HttpSecurity http) throws Exception {

    CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
    filter.setRequiresAuthenticationRequestMatcher(
        new AntPathRequestMatcher("<ログインのURL>", "POST"));
    filter.setAuthenticationManager(authenticationManagerBean());

    http
        .csrf()
        .disable()
        .authorizeRequests()
        .antMatchers("<認証不要なURL>")
        .permitAll()
        .anyRequest()
        .authenticated()
        .and()
        .logout()
        .logoutRequestMatcher(
            new AntPathRequestMatcher("<ログアウトのURL>", "POST"))
        .and()
        .addFilter(filter);
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(authenticationProvider);
  }
}

参考

10
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11