s e a r c h S e a r c h L o g i n S i g n u p
(一) T r e n d
(二) Q i i t a E n g i n e e r F e s t a 2 0 2 4
(三) Q u e s t i o n
(四) O f f i c i a l E v e n t
(五) O f f i c i a l C o l u m n
(六) s i g n p o s t C a r e e r
(七) O r g a n i z a t i o n
33
G o t o l i s t o f u s e r s w h o l i k e d
33
m o r e _ h o r i z
i n f o
M o r e t h a n 5 y e a r s h a v e p a s s e d s i n c e l a s t u p d a t e .
@ A k i h i r o T a k a m u r a
S p r i n g B o o t と S p r i n g S e c u r i t y O A u t h 2 で 自 作 O A u t h サ ー バ と 認 証 す る
● O A u t h
● s p r i n g - s e c u r i t y
● S p r i n g B o o t
L a s t u p d a t e d a t 2 0 1 6 - 0 2 - 2 2 P o s t e d a t 2 0 1 6 - 0 2 - 1 8
は じ め に
使 用 す る も の
● E c l i p s e
● G r a d l e
● 認 証 サ ー バ こ こ で 作 成 し た も の
i n s t a l l
● E c l i p s e M a r k e t P l a c e か ら
● G r a d l e I D E P a c k
● S p r i n g T o o l S u i t e
P r o j e c t s e t u p
p r o j e c t 作 成
● E c l i p s e - F i l e - N e w - P r o j e c t . . .
● G r a d l e - G r a d l e P r o j e c t
● P r o j e c t n a m e に 任 意 の 名 前
、 S a m p l e P r o j e c t に J a v a Q u i c k s t a r t
を 選 択
● 不 要 な パ ッ ケ ー ジ の 削 除
● s r c / m a i n / j a v a 配 下
● s r c / m a i n / r e s o u r c e s 配 下
● s r c / t e s t / j a v a 配 下
● s r c / t e s t / r e s o u r c e s 配 下
g r a d l e よ り 依 存 パ ッ ケ ー ジ の イ ン ス ト ー ル
● p a c k a g e v e r s i o n 情 報 を 外 出 し す る た め 、 p r o j e c t 直 下 に g r a d l e . p r o p e r t i e s
フ ァ イ ル を 作 成 し 以 下 の 記 述
g r a d l e . p r o p e r t i e s
SPRING_BOOT_VERSION = 1.3.2.RELEASE
SPRING_LOADED_VERSION = 1.2.5.RELEASE
JAVA_VERSION = 1.8
POSTGRES_VERSION = 9.4-1200-jdbc41
SPRING_CORE_VERSION = 4.2.4.RELEASE
● b u i l d . g r a d l e に 依 存 パ ッ ケ ー ジ 情 報 を 記 述
b u i l d . g r a d l e
buildscript {
repositories {
mavenCentral ()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${SPRING_BOOT_VERSION}"
classpath "org.springframework:springloaded:${SPRING_LOADED_VERSION}"
}
}
apply plugin: 'java'
apply plugin: 'spring-boot'
sourceCompatibility = "${JAVA_VERSION}"
targetCompatibility = "${JAVA_VERSION}"
repositories {
mavenCentral ()
}
jar . baseName = 'springboot-oauth-sample'
dependencies {
// for web application
compile "org.springframework.boot:spring-boot-starter-web:${SPRING_BOOT_VERSION}"
// template engine: jade
compile "com.domingosuarez.boot:spring-boot-starter-jade4j:0.3.0"
// use spring security
compile "org.springframework.boot:spring-boot-starter-security:${SPRING_BOOT_VERSION}"
// use spring security oauth2
compile "org.springframework.security.oauth:spring-security-oauth2:2.0.7.RELEASE"
// use configuration processor
compile "org.springframework.boot:spring-boot-configuration-processor:1.2.5.RELEASE"
// use spring core
compile "org.springframework:spring-core:${SPRING_CORE_VERSION}"
// use Scribe OAuth
compile "com.github.scribejava:scribejava-apis:2.2.2"
}
● P r o j e c t 右 ク リ ッ ク - G r a d l e - R e f r e s h A l l
c o d i n g
エ ン ト リ ポ イ ン ト の 作 成
● s r c / m a i n / j a v a を 右 ク リ ッ ク - N e w - P a c k a g e
今 回 p a c k a g e n a m e は s p r i n g b o o t . o a u t h .
s a m p l e
と し た
● 作 成 し た パ ッ ケ ー ジ 右 ク リ ッ ク - N e w - C l a s s - M a i n . j a v a
を 作 成 す る
package springboot.oauth.sample ;
import org.springframework.boot.SpringApplication ;
import org.springframework.boot.autoconfigure.SpringBootApplication ;
@SpringBootApplication
public class Main {
public static void main ( String [] args ) {
SpringApplication . run ( Main . class , args );
}
}
a p p l i c a t i o n 設 定
● s r c / m a i n / r e s o u r c e s / a p p l i c a t i o n . y m l を 作 成
● 事 前 に o a u t h s e r v e r 側 で c l i e n t を 作 成
● 作 成 時 に 取 得 し た c l i e n t - i d と c l i e n t - s e c r e t を 設 定
● o a u t h s e r v e r 側 の 認 証 u r l も 設 定 す る
● O A u t h 側 の 設 定 は ほ ぼ こ れ だ け で あ と は 勝 手 に 認 証 処 理 し て く れ る
server:
port: 8080
session-timeout: 1200
spring:
main:
show-banner: true
security:
basic:
enabled: false
oauth2:
client:
client-id: sampleapp
client-secret: wlpaBFBJTzmRLcLInQcIiS8ggLclLjQg
access-token-uri: http://localhost:9999/api/oauth2/token
user-authorization-uri: http://localhost:9999/api/oauth2/authorize
scope: admin,user
authorized-grant-types: authorization_code,refresh_token,client_credentials
resource:
user-info-uri: http://localhost:9999/api/profile
prefer-token-info: false
custom:
# for single sign out(global logout)
server-logout-url: http://localhost:9999/logout
# redirect url for after single signed out
server-logouted-redirect-url: http://localhost:8080/
S p r i n g s e c u r i t y の 設 定
● s r c / m a i n / j a v a / / c o n f i g / S e c u r i t y C o n f i g . j a v a
● s p r i n g s e c u r i t y の 設 定 に て 認 証 が 必 要 な u r l を 設 定 す る
●
H t t p S e c u r i t y
に セ ッ ト
● s p r i n g s e c u r i t y に 認 証 を O A u t h サ ー バ に 任 せ る よ う @ E n a b l e O A u t h 2 S s o
ア ノ テ ー シ ョ ン を つ け る
package springboot.oauth.sample.config ;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso ;
import org.springframework.context.annotation.Configuration ;
import org.springframework.security.config.annotation.web.builders.HttpSecurity ;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity ;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter ;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter ;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode ;
@Configuration
@EnableWebSecurity
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value ( "${security.oauth2.custom.server-logout-url}" ) private String serverLogoutUrl ;
@Value ( "${security.oauth2.custom.server-logouted-redirect-url}" ) private String serverLogoutedRedirectUrl ;
@Override
public void configure ( HttpSecurity http ) throws Exception {
http
. headers ()
// allow iframe
. addHeaderWriter ( new XFrameOptionsHeaderWriter ( XFrameOptionsMode . SAMEORIGIN ))
. and ()
. authorizeRequests ()
. antMatchers ( "/public/**" ). permitAll ()
. antMatchers ( "/" ). permitAll ()
. antMatchers ( "/admin/**" ). hasRole ( "ADMIN" )
. anyRequest (). authenticated ()
. and ()
. csrf ()
. csrfTokenRepository ( csrfTokenRepository ())
. and ()
. logout ()
. logoutRequestMatcher ( new AntPathRequestMatcher ( "/logout" ))
. logoutSuccessUrl ( serverLogoutUrl + "?next=" + serverLogoutedRedirectUrl )
. deleteCookies ( "JSESSIONID" )
. invalidateHttpSession ( true )
. permitAll ()
;
}
private CsrfTokenRepository csrfTokenRepository () {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository ();
repository . setHeaderName ( "X-XSRF-TOKEN" );
return repository ;
}
}
C o n t r o l l e r の 作 成
認 証 が 必 要 な U R L を 作 成
● 実 際 は S e c u r i t y C o n f i g . j a v a の 設 定 で 認 証 を 必 要 と す る U R L
● 今 回 は / p r o t e c t e d
と し た
ア ク セ ス 時 、 O A u t h サ ー バ へ 自 動 的 に 認 証 し に い き 、 さ ら に 認 証 OK 後 、 a c c e s s T o k e n を 取 得 、 p r o f i l e を 取 得 す る と こ ろ ま で 全 自 動
認 証 OK で c o n t r o l l e r に 入 っ て く る の で 認 証 情 報 を O A u t
h 2 A u t h e n t i c a t i o n
で 取 得 で き る
package springboot.oauth.sample.controller ;
import org.springframework.security.core.Authentication ;
import org.springframework.security.oauth2.provider.OAuth2Authentication ;
import org.springframework.web.bind.annotation.RequestMapping ;
import org.springframework.web.bind.annotation.RequestMethod ;
import org.springframework.web.bind.annotation.RestController ;
@RestController
@RequestMapping ( value = "/protected" )
public class ProtectedController extends BaseController {
@RequestMapping ( value = { "" , "/" }, method = RequestMethod . GET )
public Authentication index ( OAuth2Authentication authentication ) {
return authentication ;
}
}
試 し て み る
ロ グ イ ン
● ブ ラ ウ ザ か ら l o c a l h o s t : 8 0 8 0 / p r o t e c t e d に ア ク セ ス
● 自 動 的 に O A u t h サ ー バ の 認 証 ペ ー ジ に 遷 移 す る
● 認 証 OK す る と 以 下 の こ と を S p r i n g S e c u r i t y が 自 動 的 に し て く れ る
●
/ l o g i n
と い う U R L を 使 い O A u t h c o d e を 取 得
● c o d e を 使 っ て O A u t h サ ー バ に リ ク エ ス ト を 投 げ 、 a c c e s s _ t o k e n を 取 得
● a c c e s s _ t o k e n を 使 っ て O A u t h サ ー バ の p r o f i l e a p i に ア ク セ ス し て p r o f i l e を 取 得
● も と も と リ ク エ ス ト さ れ た U R L に 遷 移
ロ グ ア ウ ト
● ブ ラ ウ ザ か ら l o c a l h o s t : 8 0 8 0 / l o g o u t に ア ク セ ス
● / l o g o u t は S e c u r i t y C o n f i g で 指 定 し た U R L で 、 s p r i n g s e c u r i t y が 自 動 的 に u r l を マ ッ ピ ン グ し て く れ る
● ロ グ ア ウ ト 後 、 O A u t h サ ー バ の ロ グ ア ウ ト U R L に 飛 ば し て い る の で O A u t h サ ー バ 側 も ロ グ ア ウ ト さ れ る
● こ の 辺 は 無 理 や り だ け れ ど g l o b a l l o g o u t っ て き っ と こ う い う こ と な ん だ ろ う と 勝 手 に 解 釈
33
G o t o l i s t o f u s e r s w h o l i k e d
33
c o m m e n t 0
G o t o l i s t o f c o m m e n t s
R e g i s t e r a s a n e w u s e r a n d u s e Q i i t a m o r e c o n v e n i e n t l y
(一) Y o u g e t a r t i c l e s t h a t m a t c h y o u r n e e d s
(二) Y o u c a n e f f i c i e n t l y r e a d b a c k u s e f u l i n f o r m a t i o n
(三) Y o u c a n u s e d a r k t h e m e
W h a t y o u c a n d o w i t h s i g n i n g u p
S i g n u p L o g i n
33
G o t o l i s t o f u s e r s w h o l i k e d
33
m o r e _ h o r i z
H o w d e v e l o p e r s c o d e i s h e r e .
G u i d e & H e l p
C o n t e n t s
S N S
O u r s e r v i c e
C o m p a n y