SpringSecurityOAuth2之JWT應用示例


  本文記錄一下使用SpringSecurityOAuth2配置JWT的步驟

  1、相關知識

  OAuth協議簡介:https://www.cnblogs.com/javasl/p/13054133.html

  OAuth 2.0官網:https://oauth.net/2/

  使用SpringSecurityOAuth2默認實現OAuth2授權示例:https://www.cnblogs.com/javasl/p/13060284.html

  使用SpringSecurityOAuth2配置自定義Token實現OAuth2授權示例:https://www.cnblogs.com/javasl/p/13068613.html

  2、JWT基礎

  JWT全稱是Json Web Token,它是JSON的一個開放的Token標准。JWT有三個特點:

  1)自包含。JWT中包含着Token有意義的信息,拿到Token,解析后就能知道里面包含的信息是什么,而Spring默認生成的Token是UUID,沒有任何有意義的信息。它的信息需要根據這個Token去Redis中讀取。

  2)密簽。發出去的令牌不包含密碼等敏感信息,使用指定的秘鑰簽名。

  3)可擴展。包含的信息可以根據業務需求自己定義。

  2、構建項目

  本文使用的springboot版本是2.0.4.RELEASE,不同版本可能會有所區別。下面是主要的配置文件和類:

  1)pom依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.1.3.RELEASE</version>
</dependency>

  2)application.properties

#不需要,暫時寫死在代碼中,重構時移植到此處即可

  3)主配置類

@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic().and().csrf().disable();
    }
    @Bean("authenticationManager")
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

  4)用戶認證類

@Component
public class MyUserDetailsService implements UserDetailsService{

    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("登錄用戶名:"+username);
        String  password = passwordEncoder.encode("123456");
        return new User(username,password,true,true,true,true,
                AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_USER"));
    }
}

  5)認證服務類

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter{

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private TokenStore tokenStore;
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;    
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .tokenStore(tokenStore)//如果使用RedisTokenStore,則會把Token存入redis中,否則存在內存 .accessTokenConverter(jwtAccessTokenConverter) .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients .inMemory()//Token保存在內存中 .withClient("MyProject").secret(passwordEncoder.encode("MyProject_123"))//指明client-id和client-secret .accessTokenValiditySeconds(7200)//令牌有效時間,單位秒 .authorizedGrantTypes("refresh_token","password","authorization_code")//支持刷新令牌、密碼模式、授權碼模式 .scopes("all","read","write")//權限有哪些,如果這兩配置了該參數,客戶端發請求可以不帶參數,使用配置的參數 .redirectUris("http://127.0.0.1:8080/login"); } }

  6)JWT配置類

@Configuration
public class JwtTokenConfig {
        
    @Bean
    public TokenStore jwtTokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        accessTokenConverter.setSigningKey("shxiang");//設置秘鑰
        return accessTokenConverter;
    }
}

  說明:上一篇講到RedisTokenStore,這里又有JwtTokenStore,一定要區分開。

  a)如果TokenStore的注入類型是RedisTokenStore,則生成的JWT會存入redis中。

  b)如果TokenStore的注入類型是JwtTokenStore,或者endpoints不設置TokenStore,那么生成的JWT在內存中。

  7)啟動類

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

  4、測試驗證

  1)獲取Token成功

  

  

  2)將Token復制到https://www.jsonwebtoken.io/解析Token成功 

  

  3)根據Token獲取當前用戶信息

@GetMapping("/me")
public Object getCurrentUser(Authentication user) {
    return user;
}

  

  

  說明:上面是密碼模式的測試,授權碼模式的測試請參考上一篇的測試驗證部分。

  5、擴展自定義字段 

  1)添加Token增強類

public class MyJwtTokenEnhancer implements TokenEnhancer{

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        Map<String,Object> info = new HashMap<String, Object>();
        info.put("company","test test test");//拓展的字段
        ((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(info);
        return accessToken;
    }
}

  2)修改JwtTokenConfig.java,添加jwtTokenEnhancer方法創建bean

@Bean
@ConditionalOnMissingBean(name = "jwtTokenEnhancer")
public TokenEnhancer jwtTokenEnhancer() {
    return new MyJwtTokenEnhancer();
}

  3)修改AuthorizationServerConfig.java的configure方法,如下:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter{
    ... ...
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    @Autowired private TokenEnhancer jwtTokenEnhancer;
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
             .authenticationManager(authenticationManager)
             .userDetailsService(userDetailsService);
        
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> enhancers = new ArrayList<>(); enhancers.add(jwtTokenEnhancer); enhancers.add(jwtAccessTokenConverter); enhancerChain.setTokenEnhancers(enhancers); endpoints.tokenEnhancer(enhancerChain);
        endpoints.accessTokenConverter(jwtAccessTokenConverter);
    }
    ... ...
}

  測試結果如下:

  

  

  

  注意:拓展的字段不會封裝到用戶信息Authentication中,執行http://localhost:8080/me不會獲取到拓展字段。

  6、獲取自定義字段

  1)添加pom.xml依賴

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.7.0</version>
</dependency>

  2)測試方法如下

@GetMapping("/me")
public Object getCurrentUser(Authentication user,HttpServletRequest request) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException, UnsupportedEncodingException {
      String header = request.getHeader("Authorization");
      String token = StringUtils.substringAfter(header,"bearer ");
      Claims claims = Jwts.parser().setSigningKey("shxiang".getBytes("UTF-8"))//shxiang是秘鑰
                .parseClaimsJws(token).getBody();
      String company = (String)claims.get("company");
      System.out.println("-------company----------:"+company);
      return user;
}

  結果如下:

  

  

  7、Token的刷新

  獲取令牌的時候會獲取access_token和refresh_token,如下圖所示:

  

  使用refresh_token可以刷新Token,如下:

  

  

  當用戶使用Token訪問時,發現超時了,在無感知的情況下立馬使用refresh_token請求一個新的Token。refresh_token設置較長時間,如:

.accessTokenValiditySeconds(7200)
.refreshTokenValiditySeconds(2592000)

 


免責聲明!

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



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