実際には"ちょろっと"いかなかったのでまぁメモるよね。
ここ1週間くらい調べながら簡単な画面を作って試してみたので情報が散らかってしまわないようにまとめます。
やりたい事
独自のログインフォーム、またはGoogleアカウントを用いてログインを行う。
要点
要点は以下の通り
- SpringSecurityのWebSecurityConfigurerAdapterを拡張してOAuth2.0によるログインを有効にする
- application.ymlにGoogle Developerで生成したクライアントID/クライアントシークレットを設定する
ソースコードは
GitHubに上げています。
WebSecurityConfigurerAdapterを拡張して記述した認可が問題なければ
ログイン処理もログアウト処理もSpring様がよしなにやってくれます!素敵
解説
OAuth2.0によるログインを有効化
WebSecurityConfigurerAdapterを拡張した
WebSecurityConfigを作成します。
重要なのは33行目からある
WebSecurityConfigurerAdapter#configure(http)のOverride。
このメソッドで認証/認可の設定を行います。
WebSecurityConfig.java package example.security;
import example.service.impl.AuthenticationProviderImpl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import com.google.common.collect.Lists;
/**
* Spring Security設定クラス.
*/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/** ログインフォーム利用時の認証クラス */
@Autowired
private AuthenticationProviderImpl authenticationProvider;
/**
* SpringSecurityを用いてページアクセスの認証/認可を設定
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 認証対象外のパスを指定してアクセスを許可する
.antMatchers("/css/**", "/js/**", "/images/**").permitAll()
// ADMIN roleじゃないと/showAdministratorには入れない
.antMatchers("/showAdministrator").hasRole("ADMIN")
// それ以外は匿名アクセス禁止
.anyRequest().authenticated()
// ログアウト後にログイン画面を表示するための設定
.and().logout().logoutSuccessUrl("/login").invalidateHttpSession(true).deleteCookies("JSESSIONID").permitAll()
// フォームログインを有効化, ログインURLは"/login", ログイン成功時の遷移先は"/", ログイン失敗時の遷移先は"/login-error"
.and().formLogin().loginPage("/login").defaultSuccessUrl("/").failureUrl("/login-error")
.usernameParameter("inputUserName").passwordParameter("inputPassWord").permitAll()
// OAuth2認証を有効化, ログインURLは"/login", GrantedAuthoritiesMapperを適用する
.and().oauth2Login().loginPage("/login").permitAll().userInfoEndpoint().userAuthoritiesMapper(oauth2UserAuthoritiesMapper());
}
/**
* OAuth2.0を用いてログインした場合のユーザーの権限を設定
*/
private GrantedAuthoritiesMapper oauth2UserAuthoritiesMapper() {
// インタフェース的には複数件受け取ることができるが、実際には権限情報(ROLE_USER)の1件のみが渡される
return authorities -> {
List<GrantedAuthority> mappedAuthorities = Lists.newArrayList();
for (GrantedAuthority authority : authorities) {
// オリジナルの権限情報は引き継ぐ
mappedAuthorities.add(authority);
if (OAuth2UserAuthority.class.isInstance(authority)) {
// OAuth 2.0 Login機能でログインしたユーザに与える権限情報(ROLE_OAUTH_USER)を追加
mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_OAUTH_USER"));
}
}
return mappedAuthorities;
};
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
// 独自認証クラスを設定する
auth.authenticationProvider(authenticationProvider);
}
}
それぞれコメントを記載していますがOAuth2.0を有効とするためには
一番下にある
oauth2Login()を明記することでOAuth2.0によるログインが可能となります。
後ろに付与している
userAuthoritiesMapper()では
oauth2UserAuthoritiesMapper()メソッドを設定しており
OAuth2.0によるログインを行ったユーザーに対する認可(権限)の設定はこの場所で行われます。
因みにデフォルトの認可として
"ROLE_USER"が設定されているので
通常ユーザーでログイン + OAuth2.0によるログインを行ったユーザーに対する認可が設定できるような形となっています。
OAuth2.0を利用するサービスを設定
OAuthによるログインを行うサービスの設定をSpringBootではapplication.ymlに設定します。
application.yml spring:
security:
oauth2:
client:
registration:
google:
clientId: {生成したクライアントID}
clientSecret: {生成したクライアントシークレット}
クライアントID/クライアントシークレットはGoogleの場合
GoogleCloudPlatformのメニューから[APIとサービス]▶
認証情報から生成します。
[認証情報を作成]から[OAuthクライアントID]を選択。
[アプリケーションの種類]は今回の場合WebAppとしています。
[認証済みのリダイレクトURI]にはOAuth認証を行う際のURIを設定します。
とは言ってもこの[認証済みのリダイレクトURI]、SpringSecurityのソースコードに以下のような記述があります。
OAuth2LoginAuthenticationFilter.java public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
/**
* The default {@code URI} where this {@code Filter} processes authentication requests.
*/
public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/oauth2/code/*";
private static final String AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE = "authorization_request_not_found";
private ClientRegistrationRepository clientRegistrationRepository;
private OAuth2AuthorizedClientService authorizedClientService;
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository =
new HttpSessionOAuth2AuthorizationRequestRepository();
/**
* Constructs an {@code OAuth2LoginAuthenticationFilter} using the provided parameters.
*
* @param clientRegistrationRepository the repository of client registrations
* @param authorizedClientService the authorized client service
*/
public OAuth2LoginAuthenticationFilter(ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService authorizedClientService) {
this(clientRegistrationRepository, authorizedClientService, DEFAULT_FILTER_PROCESSES_URI);
}
......
}
SpringSecurityのOAuth2の設定から認証ページへのリダイレクトURLはデフォルトとして
/login/oauth2/code/*が設定されています。
*の部分には
application.ymlにてregistration下に設定したサービス名が入ってきます。
これらを組み合わせるとデフォルト時の[認証済みのリダイレクトURI]はlocalhostの場合
http://localhost:8080/login/oauth2/code/googleとなります。
ログインページの作成
ログインページについてはSpringSecurityの場合、
自動で生成してくれるのでOAuth2の認証がお試しできるだけで良い方は飛ばしても構いません。
(デフォルトのログインページで運用とかは流石に無いとは思うけど…)
ログインページのHTMLには27行目以降のように記述すると
SpringSecurityで読み込まれたOAuth2の設定を元にOAuth2を用いてログインするリンクが生成されます。
login.html <!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>ログイン</title>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
</head>
<body style="background-color: cornsilk;">
<h1>ログイン</h1>
<!-- ログインできなかった時のエラーメッセージ -->
<p th:if="${loginError}" style="color: #F00;">ログインに失敗しました。</p>
<form name="loginForm" th:action="@{/login}" method="post">
<label th:for="username">ユーザー名:</label>
<input name="inputUserName" type="text" title="ユーザー名"/>
<br/>
<label th:for="password">パスワード:</label>
<input name="inputPassWord" type="password" title="パスワード"/>
<br/>
<input type="submit"/>
</form>
<div>
<p>admin:管理者ユーザー</p>
<p>user:一般ユーザー</p>
</div>
<h3>Login with OAuth 2.0</h3>
<!-- OAuth 2.0 Login用のリンクを表示するエリア -->
<table>
<tr th:each="clientRegistration : ${@clientRegistrationRepository}">
<td>
<a th:href="@{/oauth2/authorization/{id}(id=${clientRegistration.registrationId})}"
th:text="|Sign in with ${clientRegistration.clientName}|">Sign in with App</a>
</td>
</tr>
</table>
</body>
</html>
OAuth2の設定を保持している
OAuth2ClientRegistrationRepositoryConfigurationの
clientRegistrationRepository()メソッドから取得しています。
表示にはSpringBootと仲良しなthymeleafを使用しています。
参考資料
コメントを追加