Spring Security 整合 JSON Web Token(JWT)

注:參考Spring Security 整合 JSON Web Token(JWT) 提升 REST 安全性,寫的特別全面,本文只是學習總結


JWT:

基于token的鑒權機制

基于token的鑒權機制類似于http協議也是無狀態的,它不需要在服務端去保留用戶的認證信息或者會話信息。這就意味著基于token認證機制的應用不需要去考慮用戶在哪一臺服務器登錄了,這就為應用的擴展提供了便利。

流程上是這樣的:

1.用戶使用用戶名密碼來請求服務器

2.服務器進行驗證用戶的信息

3.服務器通過驗證發送給用戶一個token

4.客戶端存儲token,并在每次請求時附送上這個token值

5.服務端驗證token值,并返回數據

這個token必須要在每次請求時傳遞給服務端,它應該保存在請求頭里, 另外,服務端要支持CORS(跨來源資源共享)策略,一般我們在服務端這么做就可以了Access-Control-Allow-Origin: *。

JWT的構成

第一部分我們稱它為頭部(header),第二部分我們稱其為載荷(payload, 類似于飛機上承載的物品),第三部分是簽證(signature).

header

jwt的頭部承載兩部分信息:

聲明類型,這里是jwt

聲明加密的算法 通常直接使用 HMAC SHA256

完整的頭部就像下面這樣的JSON:

{

'typ':'JWT',

'alg':'HS256'

}

然后將頭部進行base64加密(該加密是可以對稱解密的),構成了第一部分.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

playload

載荷就是存放有效信息的地方。這個名字像是特指飛機上承載的貨品,這些有效信息包含三個部分

1.標準中注冊的聲明

2.公共的聲明

3.私有的聲明

標準中注冊的聲明(建議但不強制使用) :

iss: jwt簽發者

sub: jwt所面向的用戶

aud: 接收jwt的一方

exp: jwt的過期時間,這個過期時間必須要大于簽發時間

nbf: 定義在什么時間之前,該jwt都是不可用的.

iat: jwt的簽發時間

jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。

公共的聲明

公共的聲明可以添加任何的信息,一般添加用戶的相關信息或其他業務需要的必要信息.但不建議添加敏感信息,因為該部分在客戶端可解密.

私有的聲明

私有聲明是提供者和消費者所共同定義的聲明,一般不建議存放敏感信息,因為base64是對稱解密的,意味著該部分信息可以歸類為明文信息。

定義一個payload:

{"sub":"1234567890","name":"John Doe","admin":true}

然后將其進行base64加密,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

signature

jwt的第三部分是一個簽證信息,這個簽證信息由三部分組成:

1.header (base64后的)

2.payload (base64后的)

3.secret

這個部分需要base64加密后的header和base64加密后的payload使用.連接組成的字符串,然后通過header中聲明的加密方式進行加鹽secret組合加密,然后就構成了jwt的第三部分。

// javascript

var ?encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString,'secret');

// TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

將這三部分用.連接成一個完整的字符串,構成了最終的jwt:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

注意:secret是保存在服務器端的,jwt的簽發生成也是在服務器端的,secret就是用來進行jwt的簽發和jwt的驗證,所以,它就是你服務端的私鑰,在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味著客戶端是可以自我簽發jwt了。

如何應用

一般是在請求頭里加入Authorization,并加上Bearer標注:

fetch('api/user/1',{

? ? ? ? ? headers:{

? ? ? ? ? ? ? ? ? ? 'Authorization':'Bearer '+ token?

? ? ? ? ? ? ? ? ? ? ? }

})

服務端會驗證token,如果驗證通過就會返回相應的資源。整個流程就是這樣的:

jwt-diagram

總結

優點

因為json的通用性,所以JWT是可以進行跨語言支持的,像JAVA,JavaScript,NodeJS,PHP等很多語言都可以使用。

因為有了payload部分,所以JWT可以在自身存儲一些其他業務邏輯所必要的非敏感信息。

便于傳輸,jwt的構成非常簡單,字節占用很小,所以它是非常便于傳輸的。

它不需要在服務端保存會話信息, 所以它易于應用的擴展

安全相關

不應該在jwt的payload部分存放敏感信息,因為該部分是客戶端可解密的部分。

保護好secret私鑰,該私鑰非常重要。

如果可以,請使用https協議

在SpringBoot中整合JWTSpring Security的步奏:

1.在項目中引入(本項目使用Gradle)

compile group:'org.springframework.boot',name:'spring-boot-starter-mobile',version:'1.5.4.RELEASE'

compile group:'org.springframework.boot',name:'spring-boot-starter-security',version:'1.5.4.RELEASE'

compile group:'io.jsonwebtoken',name:'jjwt',version:'0.7.0'

2.配置

目錄結構如下:

WebSecurityConfig文件:

@SuppressWarnings("SpringJavaAutowiringInspection")

@Configuration

@EnableWebSecurity

@EnableGlobalMethodSecurity(prePostEnabled=true)

public ?class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired

privateJwtAuthenticationEntryPointunauthorizedHandler;

@Autowired

?private UserDetailsService userDetailsService;

@Autowired

public void configureAuthentication(AuthenticationManagerBuilder ?authenticationManagerBuilder) throws Exception {

? authenticationManagerBuilder

? ? ? ? ?.userDetailsService(this.userDetailsService)

? ? ? ? ?.passwordEncoder(passwordEncoder());

? ? ?}

@Bean

public PasswordEncoder passwordEncoder() {

return new BCryptPasswordEncoder();

? ? }

@Bean

public ?JwtAuthenticationTokenFilter authenticationTokenFilterBean()throwsException {

? ? ? ?return newJwtAuthenticationTokenFilter();

? ? ?}

@Override

protected voidconfigure(HttpSecurity httpSecurity)throwsException {

httpSecurity

// we don't need CSRF because our token is invulnerable

.csrf().disable()

.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()

// don't create session

.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

.authorizeRequests()

//.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()

// allow anonymous resource requests

.antMatchers(

HttpMethod.GET,

"/",

"/*.html",

"/favicon.ico",

"/**/*.html",

"/**/*.css",

"/**/*.js"

).permitAll()

.antMatchers("/auth/**").permitAll()

.anyRequest().authenticated();

// Custom JWT based security filter

httpSecurity

?.addFilterBefore(authenticationTokenFilterBean(),UsernamePasswordAuthenticationFilter.class); ?

? // disable page caching

? httpSecurity.headers().cacheControl();

? ? ? }







最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容