Oauth2-授權碼模式與密碼模式獲取jwt令牌,以及校驗、刷新jwt令牌


Oauth2查詢的存有客戶端(不是個人用戶,而是第三方登錄時 人-客戶端-第三方的那個客戶端)賬號密碼的表必須叫oauth_client_details

Oauth2有以下授權模式:
  1.授權碼模式(Authorization Code)
  2.隱式授權模式(Implicit)
  3.密碼模式(Resource Owner Password Credentials)
  4.客戶端模式(Client credentials)

 

導入day09資源里的changgou-user-oath這個模塊,其中的關鍵是這四個配置類,和引導類

 1

 
         
package com.changgou.oauth.config;

import com.changgou.oauth.util.UserJwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;
import org.springframework.stereotype.Component;

import java.util.LinkedHashMap;
import java.util.Map;


@Configuration @EnableAuthorizationServer
class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { //數據源,用於從數據庫獲取數據進行認證操作,測試可以從內存中獲取 @Autowired private DataSource dataSource; //jwt令牌轉換器 @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; //SpringSecurity 用戶自定義授權認證類 @Autowired UserDetailsService userDetailsService; //授權認證管理器 @Autowired AuthenticationManager authenticationManager; //令牌持久化存儲接口 @Autowired TokenStore tokenStore; @Autowired private CustomUserAuthenticationConverter customUserAuthenticationConverter; /*** * 客戶端信息配置 * @param clients * @throws Exception */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.jdbc(dataSource).clients(clientDetails()); } /*** * 授權服務器端點配置 * @param endpoints * @throws Exception */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.accessTokenConverter(jwtAccessTokenConverter) .authenticationManager(authenticationManager)//認證管理器 .tokenStore(tokenStore) //令牌存儲 .userDetailsService(userDetailsService); //用戶信息service } /*** * 授權服務器的安全配置 * @param oauthServer * @throws Exception */ @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.allowFormAuthenticationForClients() .passwordEncoder(new BCryptPasswordEncoder()) .tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()"); } //讀取密鑰的配置 @Bean("keyProp") public KeyProperties keyProperties(){ return new KeyProperties(); } @Resource(name = "keyProp") private KeyProperties keyProperties; //客戶端配置 @Bean public ClientDetailsService clientDetails() { return new JdbcClientDetailsService(dataSource); } @Bean @Autowired public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) { return new JwtTokenStore(jwtAccessTokenConverter); } /**** * JWT令牌轉換器 * @param customUserAuthenticationConverter * @return */ @Bean public JwtAccessTokenConverter jwtAccessTokenConverter(CustomUserAuthenticationConverter customUserAuthenticationConverter) { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); KeyPair keyPair = new KeyStoreKeyFactory( keyProperties.getKeyStore().getLocation(), //證書路徑 changgou.jks keyProperties.getKeyStore().getSecret().toCharArray()) //證書秘鑰 changgouapp .getKeyPair( keyProperties.getKeyStore().getAlias(), //證書別名 changgou keyProperties.getKeyStore().getPassword().toCharArray()); //證書密碼 changgou converter.setKeyPair(keyPair); //配置自定義的CustomUserAuthenticationConverter DefaultAccessTokenConverter accessTokenConverter = (DefaultAccessTokenConverter) converter.getAccessTokenConverter(); accessTokenConverter.setUserTokenConverter(customUserAuthenticationConverter); return converter; } }

package
com.changgou.oauth.config; import com.changgou.oauth.util.UserJwt; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter; import org.springframework.stereotype.Component; import java.util.LinkedHashMap; import java.util.Map; @Component public class CustomUserAuthenticationConverter extends DefaultUserAuthenticationConverter { @Autowired UserDetailsService userDetailsService; @Override public Map<String, ?> convertUserAuthentication(Authentication authentication) { LinkedHashMap response = new LinkedHashMap(); String name = authentication.getName(); response.put("username", name); Object principal = authentication.getPrincipal(); UserJwt userJwt = null; if(principal instanceof UserJwt){ userJwt = (UserJwt) principal; }else{ //refresh_token默認不去調用userdetailService獲取用戶信息,這里我們手動去調用,得到 UserJwt UserDetails userDetails = userDetailsService.loadUserByUsername(name); userJwt = (UserJwt) userDetails; } response.put("name", userJwt.getName()); response.put("id", userJwt.getId()); if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) { response.put("authorities", AuthorityUtils.authorityListToSet(authentication.getAuthorities())); } return response; } }

 3

package com.changgou.oauth.config;

import com.changgou.oauth.util.UserJwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

/*****
 * 自定義授權認證類
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    ClientDetailsService clientDetailsService;

    /****
     * 自定義授權認證
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //取出身份,如果身份為空說明沒有認證
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        //沒有認證統一采用httpbasic認證,httpbasic中存儲了client_id和client_secret,開始認證client_id和client_secret
        if(authentication==null){
            ClientDetails clientDetails = clientDetailsService.loadClientByClientId(username);
            if(clientDetails!=null){
                //秘鑰
                String clientSecret = clientDetails.getClientSecret();
                //靜態方式
                //return new User(username,new BCryptPasswordEncoder().encode(clientSecret), AuthorityUtils.commaSeparatedStringToAuthorityList(""));
                //數據庫查找方式
                return new User(username,clientSecret, AuthorityUtils.commaSeparatedStringToAuthorityList(""));
            }
        }

        if (StringUtils.isEmpty(username)) {
            return null;
        }

        //根據用戶名查詢用戶信息
        String pwd = new BCryptPasswordEncoder().encode("itheima");
        //創建User對象
        String permissions = "goods_list,seckill_list";
        UserJwt userDetails = new UserJwt(username,pwd,AuthorityUtils.commaSeparatedStringToAuthorityList(permissions));
        return userDetails;
    }
}

 4

package com.changgou.oauth.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
@Order(-1)
class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /***
     * 忽略安全攔截的URL
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                "/oauth/login",
                "/oauth/logout");
    }

    /***
     * 創建授權管理認證對象
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        AuthenticationManager manager = super.authenticationManagerBean();
        return manager;
    }

    /***
     * 采用BCryptPasswordEncoder對密碼進行編碼
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /****
     *
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .httpBasic()        //啟用Http基本身份驗證
                .and()
                .formLogin()       //啟用表單身份驗證
                .and()
                .authorizeRequests()    //限制基於Request請求訪問
                .anyRequest()
                .authenticated();       //其他請求都需要經過驗證

    }
}

 

授權碼授權流程(獲取jwt令牌)

 

1、客戶端請求第三方授權

 

2、用戶同意給客戶端授權

 

3、客戶端獲取到授權碼,請求認證服務器申請 令牌

 

4、認證服務器向客戶端響應令牌

 

5、客戶端請求資源服務器的資源,資源服務校驗令牌合法性,完成授權

 

6、資源服務器返回受保護資源

 

1 獲取授權碼

此時數據庫里的的數據為  其中changgou的密碼為加密后的changgou

在瀏覽器地址欄輸入

http://localhost:9200/oauth/authorize?client_id=changgou&response_type=code&scop=app&redirect_uri=http://localhost

  這個地址是oauth2內部封裝的,不是我們自己寫的

賬號密碼都是changgou  這個賬號密碼不是用戶個人的密碼,而是第三方平台中保存的本系統的賬號和密碼

進入后點擊授權,地址欄中的http://localhost/?code=qquEOA code就是授權碼

這個授權碼生成后只能使用一次

2 獲取令牌

在postman中,編輯post請求

http://localhost:9200/oauth/token
這個地址是oauth2內部封裝的,不是我們自己寫

需要在body中攜帶三個參數(form-data)

  grant_type : authorization_code 授權類型,authorization_code代表授權碼模式

  code: 授權碼  上一步獲取的授權碼

  redirect_uri:  申請授權碼時的跳轉url  和申請授權碼時路徑中的redirect_uri的值一致,此處為http://localhost

然后需要進行httpBasic認證,basic認證是一種認證方式,將客戶端id和客戶端密碼按照“客戶端ID:客戶端密碼”的格式拼接,並用base64編 碼,放在header中請求服務端

在postman中選擇Authorization,type選擇basicAuth,右側填入客戶端的用戶名和密碼(都是changgou)

然后就可以發送請求了

獲取到的值為

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJhcHAiXSwibmFtZSI6bnVsbCwiaWQiOm51bGwsImV4cCI6MTYxMDgxOTQwNCwianRpIjoiMGM0YjA1YmItOTdhOS00ZjM2LWIzNWItOWFhMTQzYzlhNTk1IiwiY2xpZW50X2lkIjoiY2hhbmdnb3UiLCJ1c2VybmFtZSI6ImNoYW5nZ291In0.NcInwpPDeSsS24-cIh6KBFuqGBht0oy2F6mHovRwvMpJMSAHCuwpIbt3Cw3n63lKVPzqeRYhX2iOVfj2qATBIuu98DTCLkrReoEhvW6khjpW1dMJNWubdFqfPQfDmE8AcSuCGX__2pivsRUVtiew6liAxqw-GL5rD6ClTrJpTcpblo3-5nk-YMrSvAoj_xnBO4WVWh1JQJ-C_NIr0s0VuEAuKH_UZLrMGLDUyHDDk07kLiAP44gRCA1oMjBVF-OvK-9NYueetiy92T9gA8vDY01jU3SYdSOm-_Ta6xVPVY2pt6uXvzSFZ3qvCrbRK4jwnMFH-eE72vM7mhLoRPhf_g",
    "token_type": "bearer",
    "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJhcHAiXSwiYXRpIjoiMGM0YjA1YmItOTdhOS00ZjM2LWIzNWItOWFhMTQzYzlhNTk1IiwibmFtZSI6bnVsbCwiaWQiOm51bGwsImV4cCI6MTYxMDgxOTQwNCwianRpIjoiOTNjYWZiYTgtMzE5Zi00OTI2LWEwNTMtNzg1YTVmMmQyZTEzIiwiY2xpZW50X2lkIjoiY2hhbmdnb3UiLCJ1c2VybmFtZSI6ImNoYW5nZ291In0.EgPYoKMWaBUvvSHq6Gx1SX560aUJ6N3nQg28w2EyPPACHa9k0nwn9zCbeTkVSxIfXKc7q3J6f1PqpG1eMRkSYjitOZFwvSldH91__Ij33noOf4kUbOCEfBISDtY-vcXdpb25TdkH7gTahNUMKQU6N-DnL3l3Smm22-WkDeRqpNXFY2atn36sMYX_P5Nid-0Vcf6jQKwkC5N6DD38WgEOrFsXLi7JawN7833XEDMGxLZVirAPBLWFJNLtuSk-BfIF38RsdhEiiEE1AYcQJiB0ZCVf7D9umYZ_U4wNpHTZFKODUuUurLSBDu1Fkk4ybRT06-W4aAov8xSFeaWaB61vlg",
    "expires_in": 43199,
    "scope": "app",
    "jti": "0c4b05bb-97a9-4f36-b35b-9aa143c9a595"
}

access_token就是jwt令牌

refresh_token是刷新令牌,用於在令牌快過期時重置過期時間

 

expires_in 是過期時間

jti是jwt令牌的短標識,和access_token是一一對應的,我們可以把jwt令牌放進redis(jti=jwt的map),把jti放在cookie里,以節省空間

3 校驗令牌 

發送get請求

  http://localhost:9200/oauth/check_token?token= jwt令牌值,不帶引號

  這個地址是oauth2內部封裝的,不是我們自己寫

正常解析說明校驗成功

4 刷新令牌

刷新令牌不會產生新令牌,而是重置一個令牌的死亡時間

刷新令牌不需要授權碼,不需要賬號和密碼,只需要一個refresh_token,客戶端id和客戶端密碼

在postman中,編輯post請求

http://localhost:9200/oauth/token
這個地址是oauth2內部封裝的,不是我們自己寫

需要在body中攜帶兩個參數(form-data)

  grant_type : refresh_token 授權類型,refresh_token代表刷新令牌

  refresh_token: 刷新令牌的值 注意不是jwt令牌,而是獲取jwt令牌時一同獲取的refresh_token

完成請求后,expires_in的值和初次獲取令牌時一致

密碼授權流程(獲取jwt令牌)

密碼模式(Resource Owner Password Credentials)與授權碼模式的區別是申請令牌不再使用授權碼

而是直接 通過用戶名和密碼即可申請令牌。

 

在postman中,編輯post請求

http://localhost:9200/oauth/token
這個地址是oauth2內部封裝的,不是我們自己寫

需要在body中攜帶三個參數(form-data)

grant_type: password password代表授權類型為密碼模式

username:  賬號   注意這個賬號密碼時用戶個人在本系統注冊的賬號和密碼

password: 密碼   目前賬號密碼都是itheima,這和本文中開頭導入的四個類中的第三個UserDetailsServiceImpl中的假數據有關,后期要改為從數據庫中查

 

然后需要進行httpBasic認證,basic認證是一種認證方式,將客戶端id和客戶端密碼按照“客戶端ID:客戶端密碼”的格式拼接,並用base64編 碼,放在header中請求服務端

在postman中選擇Authorization,type選擇basicAuth,右側填入客戶端的用戶名和密碼(都是changgou)

 

 然后進行請求

即可獲取相關令牌


 

1

2

1


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM