Spring Security 多過(guò)濾鏈的使用詳解
在我們實(shí)際的開(kāi)發(fā)過(guò)程中,有些時(shí)候可能存在這么一些情況,某些api 比如: /api/** 這些是給App端使用的,數(shù)據(jù)的返回都是以JSON的格式返回,且這些API的認(rèn)證方式都是使用的TOKEN進(jìn)行認(rèn)證。而除了 /api/** 這些API之外,都是給網(wǎng)頁(yè)端使用的,需要使用表單認(rèn)證,給前端返回的都是某個(gè)頁(yè)面。
二、需求1、給客戶(hù)端使用的api 攔截 /api/**所有的請(qǐng)求。 /api/**的所有請(qǐng)求都需要ROLE_ADMIN的角色。 從請(qǐng)求頭中獲取 token,只要獲取到token的值,就認(rèn)為認(rèn)證成功,并賦予ROLE_ADMIN到角色。 如果沒(méi)有權(quán)限,則給前端返回JSON對(duì)象 {message:'您無(wú)權(quán)限訪問(wèn)'} 訪問(wèn) /api/userInfo端點(diǎn) 請(qǐng)求頭攜帶 token 可以訪問(wèn)。請(qǐng)求頭不攜帶token不可以訪問(wèn)。2、給網(wǎng)站使用的api 攔截 所有的請(qǐng)求,但是不處理/api/**開(kāi)頭的請(qǐng)求。 所有的請(qǐng)求需要ROLE_ADMIN的權(quán)限。 沒(méi)有權(quán)限,需要使用表單登錄。 登錄成功后,訪問(wèn)了無(wú)權(quán)限的請(qǐng)求,直接跳轉(zhuǎn)到百度去。 構(gòu)建2個(gè)內(nèi)建的用戶(hù) 用戶(hù)一: admin/admin 擁有 ROLE_ADMIN 角色用戶(hù)二:dev/dev 擁有 ROLE_DEV 角色 訪問(wèn) /index 端點(diǎn) admin 用戶(hù)訪問(wèn),可以訪問(wèn)。dev 用戶(hù)訪問(wèn),不可以訪問(wèn),權(quán)限不夠。三、實(shí)現(xiàn)方案方案一:直接拆成多個(gè)服務(wù),其中 /api/** 的成為一個(gè)服務(wù)。非/api/**的拆成另外一個(gè)服務(wù)。各個(gè)服務(wù)使用自己的配置,互不影響。
方案二在同一個(gè)服務(wù)中編寫(xiě)。不同的請(qǐng)求使用不同的SecurityFilterChain來(lái)實(shí)現(xiàn)。
經(jīng)過(guò)考慮,此處采用方案二來(lái)實(shí)現(xiàn),因?yàn)榉桨敢缓?jiǎn)單,使用方案二實(shí)現(xiàn),也可以記錄下在同一個(gè)項(xiàng)目中 通過(guò)使用多條過(guò)濾器鏈,因?yàn)椴⒉皇撬械臅r(shí)候,都是可以分成多個(gè)項(xiàng)目的。
擴(kuò)展:
1、Spring Security SecurityFilterChain 的結(jié)構(gòu)
2、控制 SecurityFilterChain 的執(zhí)行順序
使用 org.springframework.core.annotation.Order 注解。
3、查看是怎樣選擇那個(gè) SecurityFilterChain 的
查看 org.springframework.web.filter.DelegatingFilterProxy#doFilter方法
四、實(shí)現(xiàn)1、app 端 Spring Security 的配置package com.huan.study.security.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.annotation.Order;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.security.authentication.TestingAuthenticationToken;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.core.Authentication;import org.springframework.security.core.authority.AuthorityUtils;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.web.SecurityFilterChain;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import org.springframework.util.StringUtils;import javax.servlet.http.HttpServletRequest;import java.nio.charset.StandardCharsets;/** * 給 app 端用的 Security 配置 * * @author huan.fu 2021/7/13 - 下午9:06 */@Configurationpublic class AppSecurityConfig { /** * 處理 給 app(前后端分離) 端使用的過(guò)濾鏈 * 以 json 的數(shù)據(jù)格式返回給前端 */ @Bean @Order(1) public SecurityFilterChain appSecurityFilterChain(HttpSecurity http) throws Exception {// 只處理 /api 開(kāi)頭的請(qǐng)求return http.antMatcher('/api/**').authorizeRequests()// 所有以 /api 開(kāi)頭的請(qǐng)求都需要 ADMIN 的權(quán)限 .antMatchers('/api/**') .hasRole('ADMIN') .and()// 捕獲到異常,直接給前端返回 json 串.exceptionHandling() .authenticationEntryPoint((request, response, authException) -> {response.setStatus(HttpStatus.UNAUTHORIZED.value());response.setCharacterEncoding(StandardCharsets.UTF_8.name());response.setContentType(MediaType.APPLICATION_JSON.toString());response.getWriter().write('{'message:':'您無(wú)權(quán)訪問(wèn)01'}'); }) .accessDeniedHandler((request, response, accessDeniedException) -> {response.setStatus(HttpStatus.UNAUTHORIZED.value());response.setCharacterEncoding(StandardCharsets.UTF_8.name());response.setContentType(MediaType.APPLICATION_JSON.toString());response.getWriter().write('{'message:':'您無(wú)權(quán)訪問(wèn)02'}'); }) .and()// 用戶(hù)認(rèn)證.addFilterBefore((request, response, chain) -> { // 此處可以模擬從 token 中解析出用戶(hù)名、權(quán)限等 String token = ((HttpServletRequest) request).getHeader('token'); if (!StringUtils.hasText(token)) {chain.doFilter(request, response);return; } Authentication authentication = new TestingAuthenticationToken(token, null, AuthorityUtils.createAuthorityList('ROLE_ADMIN')); SecurityContextHolder.getContext().setAuthentication(authentication); chain.doFilter(request, response);}, UsernamePasswordAuthenticationFilter.class).build(); }}
2、網(wǎng)站端 Spring Secuirty 的配置
package com.huan.study.security.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.annotation.Order;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;import org.springframework.security.core.authority.AuthorityUtils;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.web.SecurityFilterChain;/** * 給 網(wǎng)站 應(yīng)用的安全配置 * * @author huan.fu 2021/7/14 - 上午9:09 */@Configurationpublic class WebSiteSecurityFilterChainConfig { /** * 處理 給 webSite(非前后端分離) 端使用的過(guò)濾鏈 * 以 頁(yè)面 的格式返回給前端 */ @Bean @Order(2) public SecurityFilterChain webSiteSecurityFilterChain(HttpSecurity http) throws Exception {AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);// 創(chuàng)建用戶(hù)authenticationManagerBuilder.inMemoryAuthentication().withUser('admin') .password(new BCryptPasswordEncoder().encode('admin')) .authorities(AuthorityUtils.commaSeparatedStringToAuthorityList('ROLE_ADMIN')) .and().withUser('dev') .password(new BCryptPasswordEncoder().encode('dev')) .authorities(AuthorityUtils.commaSeparatedStringToAuthorityList('ROLE_DEV')) .and().passwordEncoder(new BCryptPasswordEncoder());// 只處理 所有 開(kāi)頭的請(qǐng)求return http.antMatcher('/**').authorizeRequests()// 所有請(qǐng)求都必須要認(rèn)證才可以訪問(wèn) .anyRequest() .hasRole('ADMIN') .and()// 禁用csrf.csrf() .disable()// 啟用表單登錄.formLogin() .permitAll() .and()// 捕獲成功認(rèn)證后無(wú)權(quán)限訪問(wèn)異常,直接跳轉(zhuǎn)到 百度.exceptionHandling() .accessDeniedHandler((request, response, exception) -> {response.sendRedirect('http://www.baidu.com'); }) .and().build(); } /** * 忽略靜態(tài)資源 */ @Bean public WebSecurityCustomizer webSecurityCustomizer( ){return web -> web.ignoring().antMatchers('/**/js/**').antMatchers('/**/css/**'); }}
3、控制器寫(xiě)法
/** * 資源控制器 * * @author huan.fu 2021/7/13 - 下午9:33 */@Controllerpublic class ResourceController { /** * 返回用戶(hù)信息 */ @GetMapping('/api/userInfo') @ResponseBody public Authentication showUserInfoApi() {return SecurityContextHolder.getContext().getAuthentication(); } @GetMapping('/index') public String index(Model model){model.addAttribute('username','張三');return 'index'; }}
4、引入jar包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>五、實(shí)現(xiàn)效果1、app 有權(quán)限訪問(wèn) api
訪問(wèn)無(wú)權(quán)限的API直接跳轉(zhuǎn)到 百度 首頁(yè)。
六、完整代碼https://gitee.com/huan1993/Spring-Security/tree/master/multi-security-filter-chain
到此這篇關(guān)于Spring Security 多過(guò)濾鏈的使用詳解的文章就介紹到這了,更多相關(guān)Spring Security 多過(guò)濾鏈 內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. ASP 百度主動(dòng)推送代碼范例2. PHP中file_get_contents設(shè)置header請(qǐng)求頭,curl傳輸選項(xiàng)參數(shù)詳解說(shuō)明3. 如何用Python獲取計(jì)算機(jī)名,ip地址,mac地址4. JSP頁(yè)面跳轉(zhuǎn)方法大全5. .NET 6 跨服務(wù)器聯(lián)表查詢(xún)操作MySql、Oracle、SqlServer等相互聯(lián)表6. springmvc 結(jié)合ajax批量新增的實(shí)現(xiàn)方法7. HTML5實(shí)戰(zhàn)與剖析之觸摸事件(touchstart、touchmove和touchend)8. ASP.NET MVC實(shí)現(xiàn)登錄后跳轉(zhuǎn)到原界面9. 前端從瀏覽器的渲染到性能優(yōu)化10. 初試WAP之wml+ASP查詢(xún)
