github源碼:
https://github.com/gyb123456/spring-security5-jwt,最煩那些寫文檔只截圖一半還不給源碼的人呛讲,要不你就截全圖禾怠,要不就給源碼!
前言:
需要研究spring-security和jwt技術(shù)圣蝎,搞了幾天刃宵,現(xiàn)把成果記錄下
環(huán)境
Springboot:2.1.3.RELEASE
spring-security:5.1.4.RELEASE
前后端分離
所需依賴3個衡瓶,具體pom見源碼
<!--spring-security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--jwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
spring-security原理
從網(wǎng)上的各種不同方法綜合一下形成了這一套完整框架徘公,源碼里每個類各司其職,很清晰知道是干什么的哮针,現(xiàn)在講一下spring-security原理及工作流程
圖片中各個類的作用:
1 JwtUser類:實現(xiàn)Springsecurity的UserDetails類关面,此類必須有三個屬性
private String username;
private String password;
//權(quán)限,類如ROLE_ADMIN
private Collection<? extends GrantedAuthority> authorities;
2 JwtUtils:jwt工具類
3 MD5Util:密碼加密工具類
4 MyAccessDeniedHandler:實現(xiàn)Springsecurity的AccessDeniedHandler十厢,Spring security權(quán)限不足處理類
5 MyAuthenticationException:實現(xiàn)Springsecurity的AuthenticationEntryPoint等太,Spring security其他異常處理類,比如請求路徑不存在等
6 MyAuthenticationFailHandler: 實現(xiàn)Springsecurity的AuthenticationFailureHandler,Spring security登錄失敗處理類
7 MyAuthenticationSuccessHandler:實現(xiàn)Springsecurity的AuthenticationSuccessHandler蛮放,Spring security登錄成功處理類
8 MyJwtTokenFilter: 繼承Springsecurity的OncePerRequestFilter缩抡,token 過濾器,在這里解析token包颁,拿到該用戶角色瞻想,設(shè)置到springsecurity的上下文環(huán)境中压真,讓springsecurity自動判斷權(quán)限
9 MyUserDetailsService:實現(xiàn)Springsecurity的UserDetailsService,根據(jù)用戶名獲取數(shù)據(jù)庫該用戶信息蘑险,spring security在登錄時自動調(diào)用
10 WebSecurityConfig: Spring security的總配置類滴肿,配置密碼驗證規(guī)則、攔截的url佃迄、登錄接口地址泼差、登錄成功與失敗后的處理器、各種異常處理器
總結(jié):
spring-security工作流程就是
1自動識別登錄接口路徑(你配置的)呵俏,自動進(jìn)入賬戶密碼判斷方法堆缘,判斷規(guī)則可自定義,密碼驗證通過后會進(jìn)入spring security提供的MyAuthenticationSuccessHandler類柴信,在這里重寫方法套啤,用HttpServletResponse自定義返回給前端內(nèi)容,可以返回接口也可以返回html随常,隨你潜沦,我這里返回jwt。驗證失敗同理绪氛。
這里說下 Spring security的賬戶密碼判斷邏輯唆鸡,WebSecurityConfig繼承WebSecurityConfigurerAdapter,注入configureAuthentication方法枣察,重寫matches方法自定義密碼驗證規(guī)則争占,返回boolean
@Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
// 設(shè)置UserDetailsService 獲取user對象
.userDetailsService(this.userDetailsService)
// 自定義密碼驗證方法
.passwordEncoder(new PasswordEncoder() {
//這個方法沒用
@Override
public String encode(CharSequence charSequence) {
return "";
}
//自定義密碼驗證方法,charSequence:用戶輸入的密碼,s:我們查出來的數(shù)據(jù)庫密碼
@Override
public boolean matches(CharSequence charSequence, String s) {
String pass = MD5Util.string2MD5(charSequence.toString());
System.out.println("用戶輸入密碼:" + charSequence + "與數(shù)據(jù)庫相同序目?" + s.equals(pass));
return s.equals(pass);
}
});
}
2登錄成功后臂痕,前端攜帶jwt的token來訪問接口,首先會進(jìn)入我們配置的MyJwtTokenFilter 類繼承自O(shè)ncePerRequestFilter猿涨,這個OncePerRequestFilter就厲害了握童,可以說是所有filter的基類,所以最先執(zhí)行叛赚,在這里我們寫驗證jwt的邏輯澡绩,驗證通過后要告訴springsecurity,我們獲取到的用戶的權(quán)限俺附,并把權(quán)限設(shè)置到springsecurity的上下文環(huán)境中肥卡,讓它來給我們做權(quán)限的判斷,這點最重要J铝汀2郊!!是springsecurity和jwt的整合紐帶氛琢,主要代碼如下:
if(JwtUtils.isTokenExpired(Claims)){//token過期
System.out.println("token過期" + authToken);
}else{
System.out.println("token沒過期只嚣,放行" + authToken);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(UserDetails, null, UserDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
logger.info(String.format("Authenticated userDetail %s, setting security context", username));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
3springsecurity自動驗證權(quán)限后,如果權(quán)限不足艺沼,會進(jìn)入到我們配置的權(quán)限不足捕獲異常類MyAccessDeniedHandler册舞,其他錯誤的話進(jìn)入另一個異常捕獲類MyAuthenticationException,都可以自定義返回值
如何使用
具體見源碼的-- 說明.txt
本項目是Spring security5+Jwt的基礎(chǔ)實例
功能:
1:驗證用戶登錄賬號密碼是否正確障般,登錄地址可以自定義配置调鲸,密碼驗證規(guī)則也是自定義,我這里只要是123456就行挽荡,驗證成功與失敗分別有不同的類來處理藐石,登錄成功后生成JWT返回
測試方法:http://localhost:8080/user/login?username=adm&password=123456
2:配置攔截除了"/test/**"以外的所有請求,登錄地址配置好以后Spring security會自動設(shè)置為不攔截
3:配置了各種攔截器定拟,具體看代碼
使用方法
1登錄獲取jwt token(在Response Header里)于微,POST http://localhost:8080/user/login?username=adm&password=123456
2根據(jù)token 調(diào)用ROLE_ADMIN權(quán)限接口, GET http://localhost:8080/role/aaa ,返回權(quán)限不足青自,,MyUserDetailsService默認(rèn)給用戶加了ROLE_USER權(quán)限
根據(jù)token 調(diào)用無權(quán)限注解的接口, GET http://localhost:8080/role/bbb 株依,返回正常結(jié)果
根據(jù)token 調(diào)用ROLE_USER權(quán)限接口, GET http://localhost:8080/role/ccc ,返回正常結(jié)果
3調(diào)用不存在的接口延窜,返回捕獲異常http://localhost:8080/tessssst/error
歡迎大家討論點贊??
附:
JWT通用類 https://blog.csdn.net/qq_37636695/article/details/79265711
坑
跨域問題恋腕,postman能拿到值,前端頁面拿不到
1//解決spring security 跨域問題
https://blog.csdn.net/qq_35494808/article/details/82998135
2//解決X-Frame-Options: DENY問題逆瑞,jwt過期時荠藤,自定義返回值時,前端拿不到返回值获高,postman可以看到有的
在configure(HttpSecurity httpSecurity)里加上
.and().headers().frameOptions().disable();
3 在異常處理類的HttpServletResponse里加Header
public static void httpHandle(int stats ,HttpServletRequest httpServletRequest, HttpServletResponse response, String str) throws
IOException {
/**
* 注意這里加上跨域的配置
*/
response.setHeader("Access-Control-Allow-Origin",httpServletRequest.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
response.setStatus(stats);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter printWriter = response.getWriter();
response.getWriter().write(str);
printWriter.flush();
}