注:參考Spring Security 整合 JSON Web Token(JWT) 提升 REST 安全性火本,寫的特別全面伺糠,本文只是學(xué)習(xí)總結(jié)
JWT:
基于token的鑒權(quán)機(jī)制
基于token的鑒權(quán)機(jī)制類似于http協(xié)議也是無狀態(tài)的,它不需要在服務(wù)端去保留用戶的認(rèn)證信息或者會話信息明场。這就意味著基于token認(rèn)證機(jī)制的應(yīng)用不需要去考慮用戶在哪一臺服務(wù)器登錄了否过,這就為應(yīng)用的擴(kuò)展提供了便利午笛。
流程上是這樣的:
1.用戶使用用戶名密碼來請求服務(wù)器
2.服務(wù)器進(jìn)行驗(yàn)證用戶的信息
3.服務(wù)器通過驗(yàn)證發(fā)送給用戶一個(gè)token
4.客戶端存儲token,并在每次請求時(shí)附送上這個(gè)token值
5.服務(wù)端驗(yàn)證token值苗桂,并返回?cái)?shù)據(jù)
這個(gè)token必須要在每次請求時(shí)傳遞給服務(wù)端药磺,它應(yīng)該保存在請求頭里, 另外煤伟,服務(wù)端要支持CORS(跨來源資源共享)策略癌佩,一般我們在服務(wù)端這么做就可以了Access-Control-Allow-Origin: *。
JWT的構(gòu)成
第一部分我們稱它為頭部(header),第二部分我們稱其為載荷(payload, 類似于飛機(jī)上承載的物品)便锨,第三部分是簽證(signature).
header
jwt的頭部承載兩部分信息:
聲明類型围辙,這里是jwt
聲明加密的算法 通常直接使用 HMAC SHA256
完整的頭部就像下面這樣的JSON:
{
'typ':'JWT',
'alg':'HS256'
}
然后將頭部進(jìn)行base64加密(該加密是可以對稱解密的),構(gòu)成了第一部分.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
playload
載荷就是存放有效信息的地方。這個(gè)名字像是特指飛機(jī)上承載的貨品放案,這些有效信息包含三個(gè)部分
1.標(biāo)準(zhǔn)中注冊的聲明
2.公共的聲明
3.私有的聲明
標(biāo)準(zhǔn)中注冊的聲明(建議但不強(qiáng)制使用) :
iss: jwt簽發(fā)者
sub: jwt所面向的用戶
aud: 接收jwt的一方
exp: jwt的過期時(shí)間姚建,這個(gè)過期時(shí)間必須要大于簽發(fā)時(shí)間
nbf: 定義在什么時(shí)間之前,該jwt都是不可用的.
iat: jwt的簽發(fā)時(shí)間
jti: jwt的唯一身份標(biāo)識吱殉,主要用來作為一次性token,從而回避重放攻擊掸冤。
公共的聲明:
公共的聲明可以添加任何的信息厘托,一般添加用戶的相關(guān)信息或其他業(yè)務(wù)需要的必要信息.但不建議添加敏感信息,因?yàn)樵摬糠衷诳蛻舳丝山饷?
私有的聲明:
私有聲明是提供者和消費(fèi)者所共同定義的聲明贩虾,一般不建議存放敏感信息催烘,因?yàn)閎ase64是對稱解密的沥阱,意味著該部分信息可以歸類為明文信息缎罢。
定義一個(gè)payload:
{"sub":"1234567890","name":"John Doe","admin":true}
然后將其進(jìn)行base64加密,得到Jwt的第二部分考杉。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
signature
jwt的第三部分是一個(gè)簽證信息策精,這個(gè)簽證信息由三部分組成:
1.header (base64后的)
2.payload (base64后的)
3.secret
這個(gè)部分需要base64加密后的header和base64加密后的payload使用.連接組成的字符串,然后通過header中聲明的加密方式進(jìn)行加鹽secret組合加密崇棠,然后就構(gòu)成了jwt的第三部分咽袜。
// javascript
var ?encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString,'secret');
// TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
將這三部分用.連接成一個(gè)完整的字符串,構(gòu)成了最終的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
注意:secret是保存在服務(wù)器端的,jwt的簽發(fā)生成也是在服務(wù)器端的枕稀,secret就是用來進(jìn)行jwt的簽發(fā)和jwt的驗(yàn)證询刹,所以,它就是你服務(wù)端的私鑰萎坷,在任何場景都不應(yīng)該流露出去凹联。一旦客戶端得知這個(gè)secret, 那就意味著客戶端是可以自我簽發(fā)jwt了。
如何應(yīng)用
一般是在請求頭里加入Authorization哆档,并加上Bearer標(biāo)注:
fetch('api/user/1',{
? ? ? ? ? headers:{
? ? ? ? ? ? ? ? ? ? 'Authorization':'Bearer '+ token?
? ? ? ? ? ? ? ? ? ? ? }
})
服務(wù)端會驗(yàn)證token蔽挠,如果驗(yàn)證通過就會返回相應(yīng)的資源。整個(gè)流程就是這樣的:
jwt-diagram
總結(jié)
優(yōu)點(diǎn)
因?yàn)閖son的通用性瓜浸,所以JWT是可以進(jìn)行跨語言支持的澳淑,像JAVA,JavaScript,NodeJS,PHP等很多語言都可以使用。
因?yàn)橛辛藀ayload部分插佛,所以JWT可以在自身存儲一些其他業(yè)務(wù)邏輯所必要的非敏感信息杠巡。
便于傳輸,jwt的構(gòu)成非常簡單雇寇,字節(jié)占用很小氢拥,所以它是非常便于傳輸?shù)摹?/p>
它不需要在服務(wù)端保存會話信息, 所以它易于應(yīng)用的擴(kuò)展
安全相關(guān)
不應(yīng)該在jwt的payload部分存放敏感信息,因?yàn)樵摬糠质强蛻舳丝山饷艿牟糠帧?/p>
保護(hù)好secret私鑰谢床,該私鑰非常重要兄一。
如果可以,請使用https協(xié)議
在SpringBoot中整合JWTSpring Security的步奏:
1.在項(xiàng)目中引入(本項(xiàng)目使用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.配置
目錄結(jié)構(gòu)如下:
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();
? ? ? }
作者:誰在烽煙彼岸
鏈接:http://www.reibang.com/p/beea4bc9056b
來源:簡書
著作權(quán)歸作者所有识腿。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)出革,非商業(yè)轉(zhuǎn)載請注明出處。