OAuth2.0實戰(二)四種認證方式的基本使用

在OAuth2.0裏面有以下這幾種認證方式。

  • authorization_code 授權碼認證
  • client_credentials 客戶端認證
  • password 密碼認證
  • implicit 隱式授權認證
  • refresh_token 刷新密鑰

一般我們會在認證請求的grant_type參數中指定採用何種方式去進行認證。比如下面的一個鏈接,通過grant_type=authorization_code指定採用授權碼認證。

http://localhost:8080/oauth/token?grant_type=authorization_code&client_id=client&client_secret=123456&code=jRvetc&redirect_uri=http://www.baidu.com

spring-oauth2 (bearer)是基於spring-security的驗證機制,
對於第三方訪問受限資源時通過token機制來驗證
驗證steps:
在這裏插入圖片描述

通過時序圖來看一下,驗證方式:
1、發送username, password, client_id, client_secret, grant_type到server
2、server返回包括access_token, token_type, refresh_token, expires_in
3、客戶端向資源服務器發起請求後,資源服務器會首先拿接受的token去權限服務器驗證,如果驗證通過,才繼續執行請求。

下面是一些默認的端點 URL:
/oauth/authorize:授權端點
/oauth/token:令牌端點
/oauth/confirm_access:用戶確認授權提交端點
/oauth/error:授權服務錯誤信息端點
/oauth/check_token:用於資源服務訪問的令牌解析端點
/oauth/token_key:提供公有密匙的端點,如果你使用JWT令牌的話
授權端點的 URL 應該被 Spring Security 保護起來只供授權用戶訪問

下面看看具體的項目案例:

一、創建auth2_server工程

在這裏插入圖片描述

在這裏插入圖片描述

注意:名稱中不能有大寫字母

選擇依賴:
在這裏插入圖片描述

選擇創建工程的名稱和保存的目錄:
在這裏插入圖片描述

項目新建完成。

二、配置認證中心AuthorizationServer

核心是繼承AuthorizationServerConfigurerAdapter
在這裏插入圖片描述
主要是實現三個配置方法:
1、配置認證相關屬性
configure(AuthorizationServerSecurityConfigurer security)

2.配置客戶端詳情
configure(ClientDetailsServiceConfigurer clients)

3、配置認證服務的端點Endpoints相關屬性
configure(AuthorizationServerEndpointsConfigurer endpoints)

實現如下:
在實現的AuthorizationServerConfig配置類上加上@EnableAuthorizationServer註解,說明該工程作爲認證中心。

/**
 * @program: oauth2_server
 * @description: AuthorizationServer配置
 * @author: wanli
 * @create: 2019-05-22 16:00
 **/
@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    // accessToken有效期
    private int accessTokenValiditySeconds = 7200; // 兩小時
    private int refreshTokenValiditySeconds = 7200; // 兩小時

    /**
     * 配置認證客戶端ClientDetailsService
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure( ClientDetailsServiceConfigurer clients ) throws Exception {
        //這裏主要配置的是客戶端的信息,而不是認證用戶的信息
        //添加客戶端信息
        clients.inMemory()                  // 使用in-memory存儲客戶端信息
                .withClient("client")       // client_id
                .secret("{noop}123456")                   // client_secret
                .redirectUris("http://www.baidu.com")
                 .authorizedGrantTypes("authorization_code","password","client_credentials","refresh_token","implicit")// 該client允許的授權類型
                .scopes("app")                                   // 允許的授權範圍
                .accessTokenValiditySeconds(accessTokenValiditySeconds)  //有效期時間
                .refreshTokenValiditySeconds(refreshTokenValiditySeconds)
        ;
    }

    /**
     * 配置認證服務  oauthServer
     * @param oauthServer
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
        // 允許表單認證
        oauthServer.allowFormAuthenticationForClients();
        // 允許check_token訪問
        oauthServer.checkTokenAccess("permitAll()");
    }

    /**
     * 配置訪問端口endpoints
     * @param endpoints
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager())
                //允許get,post方法訪問 (默認獲取token只能post方法)
               // .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST)
        ;
    }

    @Bean
    AuthenticationManager authenticationManager() {
        AuthenticationManager authenticationManager = new AuthenticationManager() {
            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                return daoAuhthenticationProvider().authenticate(authentication);
            }
        };
        return authenticationManager;
    }


    @Bean
    public AuthenticationProvider daoAuhthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(userDetailsService());
        daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
        daoAuthenticationProvider.setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
        return daoAuthenticationProvider;
    }

    // 設置添加用戶信息,正常應該從數據庫中讀取
    @Bean
    UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
        userDetailsService.createUser(User.withUsername("user1").password("{noop}123456")
                .authorities("ROLE_USER").build());
        userDetailsService.createUser(User.withUsername("user2").password("{noop}123456")
                .authorities("ROLE_USER").build());
        return userDetailsService;
    }
}

啓動報錯:

'authorizationEndpoint' threw exception; nested exception is java.lang.NoClassDefFoundError: javax/servlet/ServletException

原因:
沒有加入spring-boot-starter-web依賴。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

啓動成功,默認端口8080

授權碼認證 authorization_code

通過瀏覽器請求:
http://localhost:8080/oauth/authorize?client_id=client1&client_secret=123456&response_type=code&redirect_uri=http://www.baidu.com

出現如下異常:
User must be authenticated with Spring Security before authorization can be completed.
在這裏插入圖片描述
原因是:
進行oauth2認證前,必須先經過Spring Security的認證。

添加 Spring Security認證

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    // 攔截所有請求,使用httpBasic方式登陸
    @Override
    protected void configure(HttpSecurity http) throws Exception {
       //攔截所有請求 通過httpBasic進行認證
        http.authorizeRequests().antMatchers("/**").fullyAuthenticated().and().httpBasic();  
    }
}

再次請求:
http://localhost:8080/oauth/authorize?client_id=client&client_secret=123456&response_type=code&redirect_uri=http://www.baidu.com
在這裏插入圖片描述

用戶名,密碼是在UserDetailsService 中配置:
user1
123456

登錄成功,出現授權頁面:
在這裏插入圖片描述

選擇授權,重定向到百度,並在鏈接後面加上了授權碼:
在這裏插入圖片描述
使用授權碼去獲取token
使用postman發起請求:
http://localhost:8080/oauth/token?grant_type=authorization_code&client_id=client&client_secret=123456&code=jRvetc&redirect_uri=http://www.baidu.com

在這裏插入圖片描述
返回結果:
在這裏插入圖片描述

客戶端認證client_credentials

post請求:
http://localhost:8080/oauth/token?grant_type=client_credentials&client_id=client&client_secret=123456

在這裏插入圖片描述

密碼認證password

post請求:
http://localhost:8080/oauth/token?username=user1&password=123456&grant_type=password&client_id=client&client_secret=123456

在這裏插入圖片描述

刷新祕鑰refresh_token

post請求
http://localhost:8080/oauth/token?grant_type=refresh_token&client_id=client&client_secret=123456&refresh_token=4695c05c-de3b-4d1d-b98f-b0df204df46b

在這裏插入圖片描述
一直出現如下異常:
在這裏插入圖片描述
後臺的異常信息爲:
Handling error: IllegalStateException, UserDetailsService is required.

解決:
在這裏插入圖片描述
刷新祕鑰refresh_token 的認證必須在AuthorizationServerEndpointsConfigurer配置中指定UserDetailsService 。不指定,對其他四種認證獲取token是沒有影響的。

隱式授權類型 implicit

適合直接在前端應用獲取token的應用

注意:implicit和authorization_code授權請求的區別主要是
implicit的response_type=token
authorization_code的response_type = code
指定然後類型是token,請求成功後,直接將token信息鏈接到重定向地址後面。所以這個認證方式的安全級別相對較低。

一、get請求:
http://localhost:8080/oauth/authorize?client_id=client2&client_secret=123456&response_type=token&redirect_uri=http://www.baidu.com

二、在界面上,授權允許訪問
在這裏插入圖片描述

三、根據配置的redirect_uri進行重定向,並在鏈接後面直接返回access_token
請求結果:
https://www.baidu.com/#access_token=d2addcfb-e29a-44ec-95c3-e4d5f2e80663&token_type=bearer&expires_in=6967&scope=app

在這裏插入圖片描述

OAuth客戶端運行在瀏覽器中(Javascript、Flash等)
瀏覽器絕對可信,因爲該類型可能會將訪問令牌泄露給惡意用戶或應用程序。

流程剖析::

其他說明:

1、如果添加spring-cloud-starter-security的依賴後,什麼都不配置,默認攔截所有請求

示例,添加/hello測試方法

@SpringBootApplication
@RestController
public class Oauth2ServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(Oauth2ServerApplication.class, args);
    }

    @ResponseBody
    @RequestMapping("hello")
    public  String hello(){
        return  "hello";
    }
}

請求http://localhost:8080/hello,會自動重定向到http://localhost:8080/login
在這裏插入圖片描述

默認用戶是:user
密碼是項目啓動隨機生成的。
Using generated security password: bd661305-b0bb-44d8-95ae-9655a649a5f0
在這裏插入圖片描述

2、如果在屬性文件中配置security的用戶屬性,那麼就不會生成默認用戶user信息
spring.security.user.name=admin
spring.security.user.password=123456

3、在WebSecurityConfig配置類中添加默認用戶
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication()
.withUser(“user”).password("{noop}123456").roles(“USER”)
.and()
.withUser(“admin”).password("{noop}123456").roles(“USER”,“ADMIN”);
}
在這裏插入圖片描述

(1)注意密碼的加密方式,這裏採用的{noop},不加密
(2)只要配置了WebSecurityConfig配置類,那麼認證失敗重定向就會從頁面,變成小彈窗
(3)在AuthorizationServerConfig和WebSecurityConfig兩個配置類中,不要出現重複的配置屬性,
比如,針對http是否攔截的配置,針對AuthenticationManager 的Bean的配置

github源碼:
https://github.com/StarlightWANLI/oauth2.git

如果你還有其他OAuth2.0認證方面的想了解的內容,可以在底下評論留言。我會盡力抽空調研下。

最後,如果我寫的文章對您能有些許幫助,請幫忙點贊、關注。
下面是我的微信公衆號,也會不定期更新一些java方面的內容,歡迎大家來撩。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章