<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Security + OAuth2 + JWT 基本使用

          共 19677字,需瀏覽 40分鐘

           ·

          2021-05-23 13:22

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

            作者 |  MyDistance 

          來源 |  urlify.cn/ZnYBvu

          1、什么是 OAuth2

          OAuth 是一個(gè)關(guān)于授權(quán)(authorization)的開放網(wǎng)絡(luò)標(biāo)準(zhǔn),使得第三方應(yīng)用可以使用該令牌在限定時(shí)間、限定范圍訪問指定資源。在全世界得到廣泛應(yīng)用,目前的版本是2.0版。

          1.1、關(guān)于 OAuth2 的幾個(gè)重要概念:

          • resource owner: 擁有被訪問資源的用戶

          • user-agent: 一般來說就是瀏覽器

          • client: 第三方應(yīng)用

          • Authorization server: 認(rèn)證服務(wù)器,用來進(jìn)行用戶認(rèn)證并頒發(fā)token

          • Resource server:資源服務(wù)器,擁有被訪問資源的服務(wù)器,需要通過token來確定是否有權(quán)限訪問

          1.2、握手流程

          明確概念后,就可以看 OAuth2 的協(xié)議握手流程,摘自RFC6749

          (A)用戶打開客戶端以后,客戶端要求用戶給予授權(quán)。

          (B)用戶同意給予客戶端授權(quán)。

          (C)客戶端使用上一步獲得的授權(quán),向認(rèn)證服務(wù)器申請令牌。

          (D)認(rèn)證服務(wù)器對客戶端進(jìn)行認(rèn)證以后,確認(rèn)無誤,同意發(fā)放令牌。

          (E)客戶端使用令牌,向資源服務(wù)器申請獲取資源。

          (F)資源服務(wù)器確認(rèn)令牌無誤,同意向客戶端開放資源

          1.3、授權(quán)模式

          oauth2根據(jù)使用場景不同,分成了4種模式

          • 授權(quán)碼模式(authorization code)

          • 簡化模式(implicit)

          • 密碼模式(resource owner password credentials)

          • 客戶端模式(client credentials)

          授權(quán)碼模式使用到了回調(diào)地址,是最為復(fù)雜的方式,通常網(wǎng)站中經(jīng)常出現(xiàn)的微博,qq第三方登錄,都會采用這個(gè)形式。簡化模式不常用。


          2、配置

          使用oauth2保護(hù)你的應(yīng)用,可以分為簡易的分為三個(gè)步驟

          • 配置資源服務(wù)器

          • 配置授權(quán)服務(wù)器

          • 配置spring security

          2.1、maven 依賴配置

          這里直接引入 spring-cloud oauth2,更加方便之后的拓展。

          <!--spring boot-->
          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.2.13.RELEASE</version>
              <relativePath/> <!-- lookup parent from repository -->
          </parent>

          <dependencies>
              <!--spring cloud oauth2-->
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-oauth2</artifactId>
              </dependency>
              <!--spring cloud security-->
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-security</artifactId>
              </dependency>
              
              <!--JWT-->
              <dependency>
                  <groupId>io.jsonwebtoken</groupId>
                  <artifactId>jjwt</artifactId>
                  <version>0.9.1</version>
              </dependency>
          </dependencies>

          <!--spring cloud-->
          <dependencyManagement>
              <dependencies>
                  <dependency>
                      <groupId>org.springframework.cloud</groupId>
                      <artifactId>spring-cloud-dependencies</artifactId>
                      <version>Greenwich.SR2</version>
                      <type>pom</type>
                      <scope>import</scope>
                  </dependency>
              </dependencies>
          </dependencyManagement>

          2.2、配置授權(quán)服務(wù)器

          這里需要進(jìn)行訪問客戶端的配置,并配置授權(quán)類型和access_token轉(zhuǎn)jwtToken。

          @Configuration
          @EnableAuthorizationServer
          public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

              @Autowired
              private BCryptPasswordEncoder bCryptPasswordEncoder;
              @Autowired
              private AuthenticationManager authenticationManager;
              @Autowired
              private UserDetailServiceImpl userDetailService;
              @Autowired
              @Qualifier("jwtTokenStore")
              private TokenStore tokenStore;
              @Autowired
              private JwtAccessTokenConverter jwtAccessTokenConverter;
              @Autowired
              private JwtTokenEnhancer jwtTokenEnhancer;

              /**
               * 配置授權(quán)類型
               *
               * @param endpoints
               * @throws Exception
               */
              @Override
              public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
                  //設(shè)置Jwt內(nèi)容增強(qiáng)
                  TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
                  List<TokenEnhancer> list = new ArrayList<>();
                  list.add(jwtTokenEnhancer);
                  list.add(jwtAccessTokenConverter);
                  tokenEnhancerChain.setTokenEnhancers(list);

                  endpoints
                          //密碼模式必須配置
                          .authenticationManager(authenticationManager)
                          //密碼模式必須配置
                          .userDetailsService(userDetailService)
                          //accessToken轉(zhuǎn)JwtToken
                          .tokenStore(tokenStore)
                          .accessTokenConverter(jwtAccessTokenConverter)
                          //jwt內(nèi)容增強(qiáng)
                          .tokenEnhancer(tokenEnhancerChain);
              }

              /**
               * 配置客戶端詳情信息
               *
               * @param clients
               * @throws Exception
               */
              @Override
              public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
                  clients.
                          //基于內(nèi)存配置
                          inMemory()
                          //客戶端ID
                          .withClient("client")
                          //密鑰
                          .secret(bCryptPasswordEncoder.encode("112233"))
                          //重定向地址
                          .redirectUris("http://www.baidu.com")
                          //授權(quán)范圍
                          .scopes("all")
                          //accessToken有效時(shí)間
                          .accessTokenValiditySeconds(60)
                          //refreshToken有效時(shí)間
                          .refreshTokenValiditySeconds(3600)
                          /**
                           * 授權(quán)類型
                           * authorization_code:授權(quán)碼模式
                           * password:密碼模式
                           * refresh_token:刷新令牌
                           */
                          .authorizedGrantTypes("authorization_code""password""refresh_token");
              }
          }

          2.3、配置資源服務(wù)器

          繼承 ResourceServerConfigurerAdapter并添加 @EnableResourceServer注解

          @Configuration
          @EnableResourceServer
          public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
              @Override
              public void configure(HttpSecurity http) throws Exception {
                  http.authorizeRequests()
                          //攔截所有請求
                          .anyRequest()
                          .authenticated()
                          .and()
                          //spring secuity提供了requestMatchers接口,等價(jià)于http.authorizeRequests().anyRequest().access("permitAll");
                          //提供資源,訪問/user需要權(quán)限認(rèn)證
                          .requestMatchers()
                          .antMatchers("/user/**");
              }
          }

          2.4、JWT 配置

          2.4.1、accessToken 轉(zhuǎn) JwtToken 配置類

          主要工作是創(chuàng)建 JwtAccessTokenConverter并設(shè)置密鑰,并注入到 Bean 管理容器中。

          /**
           * accessToken轉(zhuǎn)JwtToken配置
           */
          @Configuration
          public class JwtTokenStoreConfig {

              @Bean
              public JwtTokenStore jwtTokenStore() {
                  return new JwtTokenStore(jwtAccessTokenConverter());
              }

              @Bean
              public JwtAccessTokenConverter jwtAccessTokenConverter() {
                  JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
                  //設(shè)置jwt密鑰
                  jwtAccessTokenConverter.setSigningKey("test_key");
                  return jwtAccessTokenConverter;
              }

              @Bean
              public JwtTokenEnhancer jwtTokenEnhancer() {
                  return new JwtTokenEnhancer();
              }
          }
          2.4.2、JwtToken內(nèi)容拓展配置類

          當(dāng) accessToken 轉(zhuǎn) jwtToken時(shí),如果想往令牌中加入自定義用戶信息,例如登錄時(shí)間點(diǎn),可以配置以下類:

          /**
           * JwtToken內(nèi)容拓展配置類
           * @author Lin
           */
          public class JwtTokenEnhancer implements TokenEnhancer {
              @Override
              public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
                  Map<String, Object> map = new HashMap<>();
                  map.put("enhance""enhance info");
                  ((DefaultOAuth2AccessToken)oAuth2AccessToken).setAdditionalInformation(map);
                  return oAuth2AccessToken;
              }
          }

          2.5、配置 spring security

          /**
           * spring security配置類
           */
          @Configuration
          @EnableWebSecurity
          public class SecurityConfig extends WebSecurityConfigurerAdapter {

              /**
               * 密碼加密
               *
               * @return
               */
              @Bean
              public BCryptPasswordEncoder getPasswordEncode() {
                  return new BCryptPasswordEncoder();
              }

              /**
               * 接口請求授權(quán)
               *
               * @param http
               * @throws Exception
               */
              @Override
              protected void configure(HttpSecurity http) throws Exception {
                  http.authorizeRequests()
                          .antMatchers("/oauth/**""/login/**","/logout/**")
                          .permitAll()
                          .anyRequest()
                          .authenticated()
                          .and()
                          .formLogin()
                          .permitAll()
                          .and()
                          .csrf().disable();
              }

              @Override
              @Bean
              protected AuthenticationManager authenticationManager() throws Exception {
                  return super.authenticationManager();
              }
          }

          2.6、實(shí)現(xiàn) UserDetailsService

          實(shí)現(xiàn) UserDetailService 用于登錄驗(yàn)證,以及密碼模式下需要用到。

          @Service
          public class UserDetailServiceImpl implements UserDetailsService {

              @Autowired
              private BCryptPasswordEncoder bCryptPasswordEncoder;

              @Override
              public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                  String password = bCryptPasswordEncoder.encode("123456");
                  return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("permission1"));
              }
          }

          創(chuàng)建 User實(shí)體類如下(非必須):

          public class User implements UserDetails {

              private String username;
              private String password;
              private List<GrantedAuthority> authorities;

              public User(String username, String password, List<GrantedAuthority> authorities) {
                  this.username = username;
                  this.password = password;
                  this.authorities = authorities;
              }

              @Override
              public Collection<? extends GrantedAuthority> getAuthorities() {
                  return authorities;
              }

              @Override
              public String getPassword() {
                  return password;
              }

              @Override
              public String getUsername() {
                  return username;
              }

              @Override
              public boolean isAccountNonExpired() {
                  return true;
              }

              @Override
              public boolean isAccountNonLocked() {
                  return true;
              }

              @Override
              public boolean isCredentialsNonExpired() {
                  return true;
              }

              @Override
              public boolean isEnabled() {
                  return true;
              }
          }

          3、運(yùn)行測試

          3.1、獲取授權(quán)碼

          直接訪問 /oauth/authorize? 接口可以獲得授權(quán)碼

          在我的項(xiàng)目中訪問路徑如下:

          http://localhost:8080/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=all

          瀏覽器訪問,跳轉(zhuǎn)到http://localhost:8080/login.html默認(rèn)登錄頁,點(diǎn)擊登錄,授權(quán):

          跳轉(zhuǎn)到https://www.baidu.com/?code=XKee3V頁面,XKee3v就是獲得的授權(quán)碼。

          3.2、根據(jù)授權(quán)碼模式獲得令牌

          利用 postman 測試,訪問

          http://localhost:8080/oauth/token

          配置 Authorization 信息,即登錄客戶端的賬號和密碼;

          配置 Body 信息,grant_type的參數(shù)值是 authorization_code,authorization_code即為授權(quán)碼模式,code即為上文獲得的授權(quán)碼。

          配置完后運(yùn)行測試,返回 access_token和 refresh_token,看到 access_token成功轉(zhuǎn)為JwtToken。

          3.3、密碼模式

          密碼模式比授權(quán)碼模式簡單一點(diǎn),不需要獲得授權(quán)碼,直接忽略上文獲取授權(quán)碼的操作,只需稍微改動配置信息。

          Authorization 信息無需改動,修改 Body 信息, grant_type的參數(shù)值改為 password,代表密碼模式,填寫登錄 spring security 的賬號和密碼。

          3.4、刷新令牌

          在上文中我設(shè)置了 access_token的時(shí)效性為60秒,當(dāng)access_token失效時(shí),需要根據(jù)refresh_token獲取新的令牌。

          訪問路徑如下:

          http://localhost:8080/oauth/token

          Authorization 配置信息如下:


          Body 需要配置 grant_type的參數(shù)值為 refresh_token,代表刷新令牌,并填寫refresh_token的參數(shù)值。訪問后即可獲得新的 access_token

          3.5、根據(jù) access_token獲得資源

          訪問路徑如下:

          http://localhost:8080/user/getCurrentUser

          Header 請求頭添加 Authorization 參數(shù),并設(shè)置參數(shù)值為 bearer+空格+ access_token,即可獲得接口返回值。






          粉絲福利:Java從入門到入土學(xué)習(xí)路線圖

          ??????

          ??長按上方微信二維碼 2 秒


          感謝點(diǎn)贊支持下哈 

          瀏覽 52
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天躁日日躁AAAXXⅩ | 久草视频大香蕉 | 四虎永久在线无码视频 | 伊人久久人妻 | 午夜大屌|