Spring Security OAuth2認(rèn)證授權(quán)示例

本文介紹了如何使用Spring Security OAuth2構(gòu)建一個(gè)授權(quán)服務(wù)器來(lái)驗(yàn)證用戶身份以提供access_token昭躺,并使用這個(gè)access_token來(lái)從資源服務(wù)器請(qǐng)求數(shù)據(jù)耗美。

1.概述

OAuth2是一種授權(quán)方法魔吐,用于通過(guò)HTTP協(xié)議提供對(duì)受保護(hù)資源的訪問(wèn)祷愉。首先,OAuth2使第三方應(yīng)用程序能夠獲得對(duì)HTTP服務(wù)的有限訪問(wèn)權(quán)限湖雹,然后通過(guò)資源所有者和HTTP服務(wù)之間的批準(zhǔn)交互來(lái)讓第三方應(yīng)用程序代表資源所有者獲取訪問(wèn)權(quán)限咏闪。

1.1 角色

OAuth定義了四個(gè)角色

  • 資源所有者 - 應(yīng)用程序的用戶。
  • 客戶端 - 需要訪問(wèn)資源服務(wù)器上的用戶數(shù)據(jù)的應(yīng)用程序摔吏。
  • 資源服務(wù)器 - 存儲(chǔ)用戶數(shù)據(jù)和http服務(wù)鸽嫂,可以將用戶數(shù)據(jù)返回給經(jīng)過(guò)身份驗(yàn)證的客戶端。
  • 授權(quán)服務(wù)器 - 負(fù)責(zé)驗(yàn)證用戶的身份并提供授權(quán)令牌征讲。資源服務(wù)器接受此令牌并驗(yàn)證您的身份据某。

OAuth2的交互過(guò)程:

OAuth2機(jī)制

1.2 訪問(wèn)令牌與刷新令牌

訪問(wèn)令牌代表一個(gè)向客戶授權(quán)的字符串。令牌包含了由資源所有者授予的權(quán)限范圍和訪問(wèn)持續(xù)時(shí)間稳诚,并由資源服務(wù)器和授權(quán)服務(wù)器強(qiáng)制執(zhí)行。

授權(quán)服務(wù)器向客戶端發(fā)出刷新令牌瀑踢,用于在當(dāng)前訪問(wèn)令牌失效或過(guò)期時(shí)獲取新的訪問(wèn)令牌扳还,或獲取具有相同或更窄范圍的其他訪問(wèn)令牌,新的訪問(wèn)令牌可能具有比資源所有者授權(quán)的更短的生命周期和更少的權(quán)限橱夭。根據(jù)授權(quán)服務(wù)器的判斷氨距,發(fā)布刷新令牌是可選的。

訪問(wèn)令牌的責(zé)任是在數(shù)據(jù)到期之前訪問(wèn)它棘劣。
刷新令牌的責(zé)任是在現(xiàn)有訪問(wèn)令牌過(guò)期時(shí)請(qǐng)求新的訪問(wèn)令牌俏让。

2. OAuth2 - 授權(quán)服務(wù)器

要使用spring Security OAuth2模塊創(chuàng)建授權(quán)服務(wù)器,我們需要使用注解@EnableAuthorizationServer并擴(kuò)展AuthorizationServerConfigurerAdapter類。

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security)
            throws Exception {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients)
            throws Exception {
        clients.inMemory().withClient("clientapp")
                .secret(passwordEncoder.encode("654321"))
                .authorizedGrantTypes("password", "authorization_code",
                        "refresh_token")
                .authorities("READ_ONLY_CLIENT").scopes("read_user_info")
                .resourceIds("oauth2-resource")
                .redirectUris("http://localhost:8081/login")
                .accessTokenValiditySeconds(5000)
                .refreshTokenValiditySeconds(50000);
    }
}
  • Spring Security OAuth2會(huì)公開(kāi)了兩個(gè)端點(diǎn)首昔,用于檢查令牌(/oauth/check_token和/oauth/token_key)寡喝,這些端點(diǎn)默認(rèn)受保護(hù)denyAll()。tokenKeyAccess()和checkTokenAccess()方法會(huì)打開(kāi)這些端點(diǎn)以供使用勒奇。

  • ClientDetailsServiceConfigurer用于定義客戶端詳細(xì)的服務(wù)信息预鬓,它可以在內(nèi)存中或在數(shù)據(jù)庫(kù)中定義。

    在本例中我們使用了內(nèi)存實(shí)現(xiàn)赊颠。它具有以下重要屬性:

    • clientId - (必需)客戶端ID格二。
    • secret - (可信客戶端所需)客戶端密鑰(可選)。
    • scope - 客戶受限的范圍竣蹦。如果范圍未定義或?yàn)榭眨J(rèn)值)顶猜,則客戶端不受范圍限制。
    • authorizedGrantTypes - 授權(quán)客戶端使用的授權(quán)類型痘括。默認(rèn)值為空长窄。
    • authorities - 授予客戶的權(quán)限(常規(guī)Spring Security權(quán)限)。
    • redirectUris - 將用戶代理重定向到客戶端的重定向端點(diǎn)远寸。它必須是絕對(duì)URL抄淑。

3. OAuth2 - 資源服務(wù)器

要?jiǎng)?chuàng)建資源服務(wù)器組件,請(qǐng)使用@EnableResourceServer注解并擴(kuò)展ResourceServerConfigurerAdapter類驰后。

@Configuration
@EnableResourceServer
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/").permitAll()
            .antMatchers("/api/**").authenticated();
    }
}

以上配置啟用/api下所有端點(diǎn)的保護(hù)肆资,但可以自由訪問(wèn)其他端點(diǎn)。

資源服務(wù)器還提供了一種對(duì)用戶自己進(jìn)行身份驗(yàn)證的機(jī)制灶芝。在大多數(shù)情況下郑原,它通常是基于表單的登錄。

@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/**")
                .requestMatchers()
                .antMatchers("/oauth/authorize**", "/login**", "/error**")
            .and()
                .authorizeRequests().anyRequest().authenticated()
            .and()
                .formLogin().permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.inMemoryAuthentication().withUser("peterwanghao")
                .password(passwordEncoder().encode("123456")).roles("USER");
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

4. OAuth2保護(hù)的REST資源

本例中只創(chuàng)建了一個(gè)RESTful API夜涕,它返回登錄用戶的姓名和電子郵件犯犁。

@Controller
public class RestResource {
    @RequestMapping("/api/users/me")
    public ResponseEntity<UserInfo> profile() {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String email = user.getUsername() + "@126.com";

        UserInfo profile = new UserInfo();
        profile.setName(user.getUsername());
        profile.setEmail(email);

        return ResponseEntity.ok(profile);
    }
}
public class UserInfo {
    private String name;
    private String email;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "User [name=" + name + ", email=" + email + "]";
    }
}

5.演示

我們有一個(gè)API http://localhost:8080/api/users/me ,我們想通過(guò)第三方應(yīng)用程序來(lái)訪問(wèn)API需要OAuth2令牌女器。

5.1 從用戶獲取授權(quán)許可代碼

如上面的序列圖所示酸役,第一步是從URL獲取資源所有者的授權(quán):

http://localhost:8080/oauth/authorize?client_id=clientapp&response_type=code&scope=read_user_info

通過(guò)瀏覽器訪問(wèn)上面的URL地址,它將展現(xiàn)一個(gè)登錄頁(yè)面驾胆。提供用戶名和密碼涣澡。對(duì)于此示例,請(qǐng)使用“peterwanghao”和“123456”丧诺。

登錄

登錄后入桂,您將被重定向到授予訪問(wèn)頁(yè)面,您可以在其中選擇授予對(duì)第三方應(yīng)用程序的訪問(wèn)權(quán)限驳阎。

授權(quán)

它會(huì)重定向到URL抗愁,如:http://localhost:8081/login?code=TUXuk9 馁蒂。這里'TUXuk9'是第三方應(yīng)用程序的授權(quán)代碼。

5.2 從授權(quán)服務(wù)器獲取訪問(wèn)令牌

現(xiàn)在蜘腌,應(yīng)用程序?qū)⑹褂檬跈?quán)碼來(lái)獲取訪問(wèn)令牌沫屡。在這里,我們需要提出以下請(qǐng)求逢捺。使用此處第一步中獲得的代碼谁鳍。

curl -X POST --user clientapp:654321 http://localhost:8080/oauth/token -H "content-type: application/x-www-form-urlencoded" -d "code=TUXuk9&grant_type=authorization_code&redirect_uri=http://localhost:8081/login&scope=read_user_info"

訪問(wèn)令牌響應(yīng)

{ 
    "access_token": "168aad01-05dc-4446-9fba-fd7dbe8adb9e", 
    "token_type": "bearer", 
    "refresh_token": "34065175-1e92-4bb0-918c-a5a6ece1dc5f", 
    "expires_in": 4999, 
    "scope": "read_user_info" 
}

5.3 從資源服務(wù)器訪問(wèn)用戶數(shù)據(jù)

一旦我們有了訪問(wèn)令牌,我們就可以轉(zhuǎn)到資源服務(wù)器來(lái)獲取受保護(hù)的用戶數(shù)據(jù)劫瞳。

curl -X GET http://localhost:8080/api/users/me -H "authorization: Bearer 168aad01-05dc-4446-9fba-fd7dbe8adb9e"

獲得資源響應(yīng)

{
    "name":"peterwanghao",
    "email":"peterwanghao@126.com"
}

總結(jié)

本文講解了OAuth2授權(quán)框架的實(shí)現(xiàn)機(jī)制倘潜,通過(guò)一個(gè)例子說(shuō)明了第三方應(yīng)用程序如何通過(guò)授權(quán)服務(wù)器的授權(quán)去資源服務(wù)器上獲取受保護(hù)的數(shù)據(jù)。本例的完整代碼在GitHub上志于。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末涮因,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子伺绽,更是在濱河造成了極大的恐慌养泡,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奈应,死亡現(xiàn)場(chǎng)離奇詭異澜掩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)杖挣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門肩榕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人惩妇,你說(shuō)我怎么就攤上這事株汉。” “怎么了歌殃?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵乔妈,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我氓皱,道長(zhǎng)路召,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任波材,我火速辦了婚禮股淡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘各聘。我一直安慰自己揣非,他們只是感情好抡医,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布躲因。 她就那樣靜靜地躺著早敬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪大脉。 梳的紋絲不亂的頭發(fā)上搞监,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音镰矿,去河邊找鬼琐驴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛秤标,可吹牛的內(nèi)容都是我干的绝淡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼苍姜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼牢酵!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起衙猪,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤馍乙,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后垫释,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體丝格,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年棵譬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了显蝌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡茫船,死狀恐怖琅束,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情算谈,我是刑警寧澤涩禀,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站然眼,受9級(jí)特大地震影響艾船,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜高每,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一屿岂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鲸匿,春花似錦爷怀、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)烤惊。三九已至,卻和暖如春吁朦,著一層夾襖步出監(jiān)牢的瞬間柒室,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工逗宜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留雄右,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓纺讲,卻偏偏與公主長(zhǎng)得像擂仍,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子熬甚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容