欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring gateway配置Spring Security實(shí)現(xiàn)統(tǒng)一權(quán)限驗(yàn)證與授權(quán)示例源碼

 更新時(shí)間:2023年07月13日 14:35:53   作者:wgslucky  
這篇文章主要介紹了Spring gateway配置Spring Security實(shí)現(xiàn)統(tǒng)一權(quán)限驗(yàn)證與授權(quán),本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

在使用Spring Cloud 進(jìn)行微服務(wù),分布式開發(fā)時(shí),網(wǎng)關(guān)是請求的第一入口,所以一般把客戶端請求的權(quán)限驗(yàn)證統(tǒng)一放在網(wǎng)關(guān)進(jìn)行認(rèn)證與鑒權(quán)。因?yàn)镾pring Cloud Gateway使用是基于WebFlux與Netty開發(fā)的,所以與傳統(tǒng)的Servlet方式不同。而且網(wǎng)關(guān)一般不會(huì)直接請求數(shù)據(jù)庫,不提供用戶管理服務(wù),所以如果想在網(wǎng)關(guān)處進(jìn)行登陸驗(yàn)證與授權(quán)就需要做一些額外的開發(fā)了。

需求設(shè)求

眾所周知,一切架構(gòu)都必須按需求來設(shè)計(jì),萬能構(gòu)架基本上是不存在的,即使是像Spring Security安全架構(gòu)也只是實(shí)現(xiàn)了一個(gè)能用方式,并不是放之四海而皆準(zhǔn)的,但是一個(gè)構(gòu)架的良好擴(kuò)展性是必須的,可以讓使用者按照自己的需要進(jìn)行擴(kuò)展使用。所以為了說明本示例的實(shí)現(xiàn),先假定這樣一個(gè)需求

1,需要有一個(gè)Web網(wǎng)關(guān)服務(wù)進(jìn)行權(quán)限統(tǒng)一認(rèn)證
2,網(wǎng)關(guān)后面有一個(gè)用戶管理服務(wù),負(fù)責(zé)用戶賬號的管理
3,網(wǎng)關(guān)后面還存在其它的服務(wù),但是這些服務(wù)需要認(rèn)證成功之后才能訪問
4,需要支持同一個(gè)請求可以被多個(gè)角色訪問

服務(wù)搭建請參考源碼 https://gitee.com/wgslucky/Spring-Gateway-Security

主要技能點(diǎn)說明

修改默認(rèn)登陸頁面

在項(xiàng)目中添加完spring security依賴之后,如果不添加任何額外的配置,這時(shí)不管發(fā)送任何請求,都會(huì)跳到spring security提供的默認(rèn)登陸頁面。這顯然不是我們想要的,那么第一步就是要顯示自定義的登陸頁面。在Spring Gateway 網(wǎng)關(guān)項(xiàng)目中添加Security的配置,如下面代碼所示:

@EnableWebFluxSecurity
public class WebSecurityConfig {
    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        RedirectServerAuthenticationEntryPoint loginPoint = new RedirectServerAuthenticationEntryPoint("/xinyue-server-a/account/index");
        http.authorizeExchange().pathMatchers("/xinyue-server-a/easyui/**","/xinyue-server-a/js/**","/xinyue-server-a/account/index","/xinyue-server-a/account/login").permitAll()    
        .and().formLogin().loginPage("/xinyue-server-a/account/authen").authenticationEntryPoint(loginPoint)
        .and().authorizeExchange().anyExchange().authenticated()
        .and().csrf().disable();
        SecurityWebFilterChain chain = http.build();
        return chain;
    }
}

這里有一個(gè)容易出現(xiàn)理解錯(cuò)誤的地址,網(wǎng)上有好多示例說是直接只配置loginPage("/my/login")即可,這樣配置的話,需要你的登陸頁面,和提交登陸信息的form的action都必須是一致的,只不過,一個(gè)是get方式請求/my/login,一個(gè)是post方式請求/my/login,但是大多數(shù)據(jù)情況下,我們的登陸頁面地址,和登陸form的action地址是分離的,所以需要按我上面的方式進(jìn)行配置才可以。

 http.authorizeExchange().pathMatchers("/xinyue-server-a/easyui/**","/xinyue-server-a/js/**","/xinyue-server-a/account/index","/xinyue-server-a/account/login").permitAll()    

這個(gè)配置表示這些請求都不做驗(yàn)證,直接放過。

.and().formLogin().loginPage("/xinyue-server-a/account/authen").authenticationEntryPoint(loginPoint)

這段配置表示需要認(rèn)證的請求是/xinyue-server-a/account/authen(對手正常的Springmvc 服務(wù)來說,這個(gè)應(yīng)該是登陸時(shí)form的action請求地址),如果沒有認(rèn)證,跳轉(zhuǎn)到loginPoint設(shè)置的地址:/xinyue-server-a/account/index,即登陸頁面。

 .and().authorizeExchange().anyExchange().authenticated()

這段配置表示其它請求都必須是認(rèn)證(登陸成功)之后才可以訪問。

Spring Cloud Gateway 認(rèn)證方式

如果是微服務(wù)模式,在Spring cloud gateway網(wǎng)關(guān)處進(jìn)行用戶認(rèn)證與授權(quán)有兩種方式:

1,在Spring Cloud Gateway服務(wù)這里添加數(shù)據(jù)庫訪問,直接檢測登陸信息是否正確,如果正確,再給此用戶授權(quán)。
2,在網(wǎng)關(guān)后面專門的認(rèn)證服務(wù)進(jìn)行登陸信息認(rèn)證,如果登陸成功,在返回的Header中添加用戶認(rèn)證與授權(quán)需要的信息,然后在網(wǎng)關(guān)處理再完成認(rèn)證與授權(quán)

Ajax Post登陸與認(rèn)證

本示例采用第二種方式,首先是客戶端向xinyue-server-a服務(wù)發(fā)送登陸請求,如下面代碼所示:

<script type="text/javascript">
	function postAjax(url, json, success) {
		$.ajax({
			type : "POST",
			url : url,
			data : JSON.stringify(json),
			dataType : "json",
			contentType : "application/json",
			success : function(data) {
				if (data.code == 0) {
					success(data);
				} else {
					alert("服務(wù)器異常,請聯(lián)系開發(fā)者");
				}
			},
			error : function(data) {
				alert(url + "請求錯(cuò)誤:" + JSON.stringify(data));
			}
		});
	}
	function submitForm() {
		$("#errorTips").html("");
		var username = $("#username").val();
		var password = $("#password").val();
		var url = "/xinyue-server-a/account/login";
		var json = {
			"username" : username,
			"password" : password
		};
		postAjax(
				url,
				json,
				function(data) {
					if (data.code == 0) { //如果登陸成功,發(fā)送認(rèn)證請求
						var authUrl = "/xinyue-server-a/account/authen";
						var param = {};
						postAjax(
								authUrl,
								param,
								function(data) {
									if (data.code == 0) {//認(rèn)證成功之后,跳轉(zhuǎn)請求
										window.location.href = "/xinyue-server-a/account/main";
									} else {
										$("#errorTips").html(data.msg);
									}
								});
					} else {
						$("#errorTips").html(data.msg);
					}
				});
	}
</script>

這里使用ajax post方式向服務(wù)端發(fā)送登陸請求,如果登陸成功,然后再發(fā)送認(rèn)證請求,在網(wǎng)關(guān)處完成認(rèn)證。

登陸成功之后,返回用戶信息,緩存在網(wǎng)關(guān)session中

在本示例的源碼中,在xinyue-server-a服務(wù)中模擬用戶登陸成功,并返回此登陸用戶的信息,主要是權(quán)限信息,如下面代碼所示:

    @RequestMapping("login")
    @ResponseBody
    public Object login(HttpServletResponse response) {
        JSONObject userInfo = new JSONObject();
        userInfo.put("username", "xinyues");
        List<String> roles = new ArrayList<>();
        roles.add("Admin");
        roles.add("Dev");
        userInfo.put("roles", roles);//添加角色信息
        response.addHeader("AccountInfo", userInfo.toJSONString());//將信息放入響應(yīng)的包頭
        JSONObject result = new JSONObject();
        result.put("code", 0);
        return result;
    }

然后在網(wǎng)關(guān)處添加過濾器,攔截登陸請求的響應(yīng)信息,如下面代碼所示:

@Service
public class AuthenticationGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
    private Logger logger = LoggerFactory.getLogger(AuthenticationGatewayFilterFactory.class);
    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> chain.filter(exchange).then(Mono.fromRunnable(() -> {
                List<String> gmAccountInfoJsons = exchange.getResponse().getHeaders().get("AccountInfo");
                exchange.getResponse().getHeaders().remove("AccountInfo");//移除包頭中的用戶信息不需要返回給客戶端
                if(gmAccountInfoJsons != null && gmAccountInfoJsons.size() > 0) {
                    String gmAccountInfoJson = gmAccountInfoJsons.get(0);//獲取header中的用戶信息
                    //將信息放在session中,在后面認(rèn)證的請求中使用
                    exchange.getSession().block().getAttributes().put("AccountInfo", gmAccountInfoJson);
                }
                logger.debug("登陸返回信息:{}",gmAccountInfoJsons);
        }));
    }
}

請求認(rèn)證過濾器,AuthenticationWebFilter

當(dāng)有請求過來時(shí),AuthenticationWebFilter用來攔截認(rèn)證請求,如果客戶端是認(rèn)證請求的話,在這里實(shí)現(xiàn)對此客戶端的認(rèn)證,一般來說攔截的是登陸form中的action地址,可以從form提交的數(shù)據(jù)中獲取用戶名和密碼,然后使用用戶和密碼進(jìn)行用戶驗(yàn)證。但是本示例中并沒有使用form提交登陸,而是使用Ajax Post方式在網(wǎng)關(guān)后面的xinyue-server-a服務(wù)中進(jìn)行的登陸驗(yàn)證。在AuthenticationWebFilter中可以看到,如果是認(rèn)證請求的話,需要使用.flatMap( matchResult -> this.authenticationConverter.convert(exchange))方式從認(rèn)證請求獲取認(rèn)證需要的信息,默認(rèn)是獲取登陸的用戶名和密碼。但是我們在上面已經(jīng)將登陸信息存在session中了,所示需要重新提供一個(gè)authenticationConverter類,如下面代碼所示:

public class XinyueAuthenticationConverter extends ServerFormLoginAuthenticationConverter{
    @Override
    public Mono<Authentication> convert(ServerWebExchange exchange) {
       //從session中獲取登陸用戶信息
       String value = exchange.getSession().block().getAttribute("AccountInfo");
       if(value == null) {
           return Mono.empty();
       } else {
           List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>();
           //獲取權(quán)限信息
           List<String> roels = JSON.parseObject(value).getJSONArray("roles").toJavaList(String.class);
               roels.forEach(role->{
                  //這里必須添加前綴,參考:AuthorityReactiveAuthorizationManager.hasRole(role)
                   SimpleGrantedAuthority auth = new SimpleGrantedAuthority("ROLE_" + role);
                   simpleGrantedAuthorities.add(auth);
               });
            //添加用戶信息到spring security之中。
           XinyueAccountAuthentication  xinyueAccountAuthentication = new XinyueAccountAuthentication(null, value, simpleGrantedAuthorities);
           return Mono.just(xinyueAccountAuthentication);
       }
    }
}

然后將XinyueAuthenticationConverter添加到WebSecurityConfig配置中(完成代碼請參考源碼)

        SecurityWebFilterChain chain = http.build();
        Iterator<WebFilter>  weIterable = chain.getWebFilters().toIterable().iterator();
        while(weIterable.hasNext()) {
            WebFilter f = weIterable.next();
            if(f instanceof AuthenticationWebFilter) {
              AuthenticationWebFilter webFilter = (AuthenticationWebFilter) f;
              //將自定義的AuthenticationConverter添加到過濾器中
              webFilter.setServerAuthenticationConverter(new XinyueAuthenticationConverter());      
            }
        }

然后添加認(rèn)證成功操作,如下面代碼所示:

    @Bean
    public ReactiveAuthenticationManager reactiveAuthenticationManager() {
        return new ReactiveAuthenticationManagerAdapter((authentication)->{
            if(authentication instanceof XinyueAccountAuthentication) {
                XinyueAccountAuthentication gmAccountAuthentication = (XinyueAccountAuthentication) authentication;
                if(gmAccountAuthentication.getPrincipal() != null) {
                    authentication.setAuthenticated(true);
                    return authentication;
                } else {
                    return authentication;
                }
            } else {
                return authentication;
            }
        });
    }

到此,就可以算是認(rèn)證成功了,登陸成功之后,就會(huì)跳到轉(zhuǎn)到主頁面了。

請求權(quán)限驗(yàn)證

一般來說,在管理系統(tǒng)中,用戶擁有不同的角色,不同的角色擁有不同的權(quán)限,那么在收到請求的時(shí)候,就需要在網(wǎng)關(guān)驗(yàn)證當(dāng)前用戶是否擁有訪問這個(gè)請求的權(quán)限,或是否是某一個(gè)角色,如果是才能進(jìn)行訪問,否則返回用戶權(quán)限不足,拒絕訪問?,F(xiàn)在給下面這個(gè)請求配置必須擁有Manager權(quán)限才可以訪問

.and().authorizeExchange().pathMatchers("/xinyue-server-a/account/main").hasRole("Manager")

如果這個(gè)時(shí)候再登陸,會(huì)發(fā)現(xiàn)服務(wù)器返回Access Denied,如果配置為Dev權(quán)限

.and().authorizeExchange().pathMatchers("/xinyue-server-a/account/main").hasRole("Dev")

因?yàn)榇擞脩魮碛蠨ev權(quán)限(模擬賬號),所以可以正常訪問。

多個(gè)角色判斷

目前Spring Security提供的模式是一個(gè)請求配置一個(gè)角色,有些復(fù)雜的系統(tǒng),要求一個(gè)請求的訪問權(quán)限可以被多個(gè)角色共同擁有。這就需要我們自定義一個(gè)權(quán)限的驗(yàn)證了。比如添加如下配置:

.and().authorizeExchange().pathMatchers("/xinyue-server-a/account/main").access(new XinyueReactiveAuthorizationManager("Manager", "Dev"))

表示這個(gè)請求需要Manager或Dev其中一個(gè)角色就可以訪問。然后在XinyueReactiveAuthorizationManager中實(shí)現(xiàn)權(quán)限驗(yàn)證判斷,詳細(xì)請考參源碼

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext object) {
        return authentication
                .filter(a -> a.isAuthenticated())
                .flatMapIterable( a -> a.getAuthorities())
                .map( g-> g.getAuthority())
                .any(c->{
                    //檢測權(quán)限是否匹配
                    String[] roles = c.split(",");
                    for(String role:roles) {
                        if(authorities.contains(role)) {
                            return true;
                        }
                    }
                    return false;
                })
                .map( hasAuthority -> new AuthorizationDecision(hasAuthority))
                .defaultIfEmpty(new AuthorizationDecision(false));
    }

到此,Spring Cloud Gateway + Spring Security配置完畢,在實(shí)際應(yīng)用中,可以根據(jù)自己的需求再進(jìn)行適當(dāng)?shù)姆庋b。

源碼地址:https://gitee.com/wgslucky/Spring-Gateway-Security

到此這篇關(guān)于Spring gateway配置Spring Security實(shí)現(xiàn)統(tǒng)一權(quán)限驗(yàn)證與授權(quán)的文章就介紹到這了,更多相關(guān)Spring gateway配置Spring Security內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot攔截器無法注入redisTemplate的解決方法

    springboot攔截器無法注入redisTemplate的解決方法

    在工作中我們經(jīng)常需要做登錄攔截驗(yàn)證或者其他攔截認(rèn)證功能,但是在寫攔截器的時(shí)候發(fā)現(xiàn)redisTemplate一直無法注入進(jìn)來,本文就詳細(xì)的介紹了解決方法,感興趣的可以了解一下
    2021-06-06
  • mybatis postgresql 批量刪除操作方法

    mybatis postgresql 批量刪除操作方法

    PostgreSQL是一種特性非常齊全的自由軟件的對象-關(guān)系型數(shù)據(jù)庫管理系統(tǒng)(ORDBMS),這篇文章主要介紹了mybatis postgresql 批量刪除操作,需要的朋友可以參考下
    2020-02-02
  • SpringMVC接收與響應(yīng)json數(shù)據(jù)的幾種方式

    SpringMVC接收與響應(yīng)json數(shù)據(jù)的幾種方式

    這篇文章主要給大家介紹了關(guān)于SpringMVC接收與響應(yīng)json數(shù)據(jù)的幾種方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用springmvc具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • springboot使用Thymeleaf報(bào)錯(cuò)常見的幾種解決方案

    springboot使用Thymeleaf報(bào)錯(cuò)常見的幾種解決方案

    這篇文章主要介紹了springboot使用Thymeleaf報(bào)錯(cuò)常見的幾種解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • 詳解如何通過Java實(shí)現(xiàn)壓縮PDF文檔

    詳解如何通過Java實(shí)現(xiàn)壓縮PDF文檔

    PDF文檔是我們?nèi)粘^k公中使用最頻繁的文檔格式。但因?yàn)榇蠖鄶?shù)PDF文檔都包含很多頁面圖像或大量圖片,這就導(dǎo)致PDF文檔過大,處理起來較為麻煩。本文將介紹如何通過Java應(yīng)用程序壓縮PDF文檔,需要的可以了解一下
    2022-12-12
  • java調(diào)用遠(yuǎn)程服務(wù)器的shell腳本以及停止的方法實(shí)現(xiàn)

    java調(diào)用遠(yuǎn)程服務(wù)器的shell腳本以及停止的方法實(shí)現(xiàn)

    這篇文章主要介紹了java調(diào)遠(yuǎn)程服務(wù)器的shell腳本以及停止的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Shiro中session超時(shí)頁面跳轉(zhuǎn)的處理方式

    Shiro中session超時(shí)頁面跳轉(zhuǎn)的處理方式

    這篇文章主要介紹了Shiro中session超時(shí)頁面跳轉(zhuǎn)的處理方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • SpringBoot發(fā)送html郵箱驗(yàn)證碼功能

    SpringBoot發(fā)送html郵箱驗(yàn)證碼功能

    這篇文章主要介紹了SpringBoot發(fā)送html郵箱驗(yàn)證碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-12-12
  • SpringBoot項(xiàng)目使用yml文件鏈接數(shù)據(jù)庫異常問題解決方案

    SpringBoot項(xiàng)目使用yml文件鏈接數(shù)據(jù)庫異常問題解決方案

    在使用SpringBoot時(shí),利用yml進(jìn)行數(shù)據(jù)庫連接配置需小心數(shù)據(jù)類型區(qū)分,如果用戶名或密碼是數(shù)字,必須用雙引號包裹以識別為字符串,避免連接錯(cuò)誤,特殊字符密碼也應(yīng)用引號包裹
    2024-10-10
  • SpringBoot整合Redis實(shí)現(xiàn)訪問量統(tǒng)計(jì)的示例代碼

    SpringBoot整合Redis實(shí)現(xiàn)訪問量統(tǒng)計(jì)的示例代碼

    本文主要介紹了SpringBoot整合Redis實(shí)現(xiàn)訪問量統(tǒng)計(jì)的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02

最新評論