国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Spring Gateway + Oauth2 + Jwt網(wǎng)關(guān)統(tǒng)一鑒權(quán)

這篇具有很好參考價值的文章主要介紹了Spring Gateway + Oauth2 + Jwt網(wǎng)關(guān)統(tǒng)一鑒權(quán)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

之前文章里說過,分布式系統(tǒng)的鑒權(quán)有兩種方式,一是在網(wǎng)關(guān)進(jìn)行統(tǒng)一的鑒權(quán)操作,二是在各個微服務(wù)里單獨(dú)鑒權(quán)。

第二種方式比較常見,代碼網(wǎng)上也是很多。今天主要是說第一種方式。

1.網(wǎng)關(guān)鑒權(quán)的流程

重要前提:需要收集各個接口的uri路徑和所需權(quán)限列表的對應(yīng)關(guān)系,并存入緩存。

2.收集uri路徑和對應(yīng)權(quán)限

服務(wù)啟動的時候,執(zhí)行緩存數(shù)據(jù)的初始化操作:掃描服務(wù)內(nèi)的所有controller接口方法,利用反射,獲取方法的完整uri路徑,方法上指定注解中的權(quán)限值,再存入Redis緩存。

服務(wù)啟動時做一些操作,方法有很多,可以繼承CommandLineRunner或者其他方式。不熟悉的可以去查一下有關(guān)資料。

因?yàn)楹罄m(xù)可能會有很多微服務(wù),因此將該緩存數(shù)據(jù)的初始化的操作放在common模塊中,微服務(wù)依賴該模塊完成。

1.1.初始化方法

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ArrayUtil;
import com.eden4cloud.common.core.contant.CacheConstants;
import com.eden4cloud.common.security.anno.Perms;
import com.eden4cloud.common.security.constant.MethodTypeConstant;
import com.eden4cloud.common.security.utils.RequestUriUtils;
import com.eden4cloud.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.util.StringUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Param:
 * @Return:
 * @Date: 2022/12/3 15:31
 * @Author: Yan
 * @Description: 接口權(quán)限初始化采集,獲取數(shù)據(jù)庫中的權(quán)限標(biāo)識,沒有權(quán)限標(biāo)識的其它接口使用**表示
 */
@Slf4j
public class ApiPermsInit implements ApplicationContextAware {

    /**
     * 接口路徑及權(quán)限列表
     * 比如:/user/list<br>
     * 不支持@PathVariable格式的URI
     */
    public static List<Map<String, String>> oauthUrls = new ArrayList<>();
    Map<String, String> uriAuthMap = new HashMap<>();

    /**
     * Url參數(shù)需要解密的配置
     * 比如:/user/list?name=加密內(nèi)容<br>
     * 格式:Key API路徑  Value 需要解密的字段
     * 示列:/user/list  [name,age]
     */
    public static Map<String, List<String>> requestDecryptParamMap = new HashMap<>();

    private String applicationPath;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        this.applicationPath = ctx.getEnvironment().getProperty("spring.application.name");
        Map<String, Object> beanMap = ctx.getBeansWithAnnotation(Controller.class);
        initData(beanMap);
        if (CollectionUtil.isNotEmpty(uriAuthMap)) {
            redisTemplate.boundHashOps(CacheConstants.OAUTH_URLS).putAll(uriAuthMap);
        }
    }

    /**
     * 初始化,獲取所有接口的加解密配置狀態(tài)并保存
     *
     * @param beanMap
     */
    private void initData(Map<String, Object> beanMap) {
        if (beanMap != null) {
            beanMap.values().parallelStream().map(Object::getClass).forEach(clz -> {
                for (Method method : clz.getDeclaredMethods()) {
                    String uriKey = RequestUriUtils.getApiUri(clz, method, applicationPath);
                    //收集帶有Perms注解的api接口的uri路徑
                    Perms perms = AnnotationUtils.findAnnotation(method, Perms.class);
                    if (StringUtils.isNotEmpty(uriKey) && perms != null && ArrayUtil.isNotEmpty(perms.value())) {
                        //解析權(quán)限標(biāo)識
                        String authValue = StringUtil.join(perms.value(), StrPool.COMMA);
                        uriAuthMap.put(uriKey, authValue);
                    } else if (uriKey.startsWith(MethodTypeConstant.GET)) {
                        /* 屏蔽沒有請求方式的api接口 */
                        //沒有權(quán)限標(biāo)識的,直接使用**表示
                        uriAuthMap.put(uriKey, "**");
                    } else if (uriKey.startsWith(MethodTypeConstant.POST)) {
                        //沒有權(quán)限標(biāo)識的,直接使用**表示
                        uriAuthMap.put(uriKey, "**");
                    } else if (uriKey.startsWith(MethodTypeConstant.PUT)) {
                        //沒有權(quán)限標(biāo)識的,直接使用**表示
                        uriAuthMap.put(uriKey, "**");
                    } else if (uriKey.startsWith(MethodTypeConstant.DELETE)) {
                        //沒有權(quán)限標(biāo)識的,直接使用**表示
                        uriAuthMap.put(uriKey, "**");
                    } else {
                        //不在上述情況中的,一般為框架提供的api接口
                        log.info(uriKey);
                    }
                }
            });
        }
    }
}

1.2.對應(yīng)的工具類

import com.eden4cloud.common.security.constant.MethodTypeConstant;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.Method;

public class RequestUriUtils {

    private static final String SEPARATOR = "/";

    /**
     * 獲取接口的uri路徑
     *
     * @param clz
     * @param method
     * @param applicationPath
     * @return
     */
    public static String getApiUri(Class<?> clz, Method method, String applicationPath) {
        String methodType = "";
        StringBuilder uri = new StringBuilder();

        // 處理類路徑
        RequestMapping reqMapping = AnnotationUtils.findAnnotation(clz, RequestMapping.class);
        if (reqMapping != null && reqMapping.value().length > 0) {
            uri.append(formatUri(reqMapping.value()[0]));
        }

        //處理方法上的路徑
        GetMapping getMapping = AnnotationUtils.findAnnotation(method, GetMapping.class);
        PostMapping postMapping = AnnotationUtils.findAnnotation(method, PostMapping.class);
        RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
        PutMapping putMapping = AnnotationUtils.findAnnotation(method, PutMapping.class);
        DeleteMapping deleteMapping = AnnotationUtils.findAnnotation(method, DeleteMapping.class);

        if (getMapping != null && getMapping.value().length > 0) {
            methodType = MethodTypeConstant.GET;
            uri.append(formatUri(getMapping.value()[0]));
        } else if (postMapping != null && postMapping.value().length > 0) {
            methodType = MethodTypeConstant.POST;
            uri.append(formatUri(postMapping.value()[0]));
        } else if (putMapping != null && putMapping.value().length > 0) {
            methodType = MethodTypeConstant.PUT;
            uri.append(formatUri(putMapping.value()[0]));
        } else if (deleteMapping != null && deleteMapping.value().length > 0) {
            methodType = MethodTypeConstant.DELETE;
            uri.append(formatUri(deleteMapping.value()[0]));
        } else if (requestMapping != null && requestMapping.value().length > 0) {
            RequestMethod requestMethod = RequestMethod.GET;
            if (requestMapping.method().length > 0) {
                requestMethod = requestMapping.method()[0];
            }
            methodType = requestMethod.name().toLowerCase() + ":";
            uri.append(formatUri(requestMapping.value()[0]));
        }

        // 框架自帶的接口,返回null后,直接忽略處理
        if (uri.indexOf("${") > 0) {
            return "";
        }

        // 針對Rest請求,路徑上的請求參數(shù)進(jìn)行處理,以**代替
        int idx = uri.indexOf("{");
        if (idx > 0) {
            uri = new StringBuilder(uri.substring(0, idx)).append("**");
        }

        return methodType + SEPARATOR + applicationPath + uri;
    }

    private static String formatUri(String uri) {
        if (uri.startsWith(SEPARATOR)) {
            return uri;
        }
        return SEPARATOR + uri;
    }
}

收集結(jié)果:

oauth2.1鑒權(quán),java,分布式,開發(fā)語言

?說明:

  1. 要求一個請求的完整路徑格式為:請求方式:/服務(wù)名/類路徑/方法路徑;
  2. 請求方式必須要有,防止路徑處理后,會出現(xiàn)重復(fù),加上請求方式可以極大避免;
  3. 服務(wù)名是為了做網(wǎng)關(guān)路由使用,在配置網(wǎng)關(guān)的路由規(guī)則時,斷言的路由規(guī)則即為/服務(wù)名;代碼里獲取服務(wù)路徑的方式是:ctx.getEnvironment().getProperty("spring.application.name");如果覺得不安全可以在yml文件中自定義一個路徑名,改一下此處的獲取值即可。只需要記得一定要和網(wǎng)關(guān)的路由斷言規(guī)則匹配就行?。。。?/li>
  4. 類路徑必須有。
  5. 方法路徑必須有。方法上的第一個路徑必須是固定路徑,而不能是請求參數(shù),另外不同方法上的第一個固定路徑避免設(shè)置成相同的;也是為了防止最終出現(xiàn)請求方式相同、路徑也相同的情況。
  6. 對Rest風(fēng)格,且方法路徑上帶有路徑參數(shù)的路徑必須做特殊處理,即將路徑參數(shù)替換成**。舉例如下:原完整路徑為/user-Service/user/queryUser/{username}/{age},方法上的路徑為/queryUser/{username}/{age},不論有多少個路徑參數(shù),從第一個路徑參數(shù)開始,全部替換掉,處理為/queryUser/**,最終存入緩存所使用的完整路徑為:GET:/user-Service/user/queryUser/**。否則當(dāng)請求到達(dá)網(wǎng)關(guān),你想要根據(jù)路徑去緩存中匹配對應(yīng)的路徑時,你會發(fā)現(xiàn)沒辦法處理,因?yàn)閺恼埱蟮膗ri路徑上你是看不出來哪是固定路徑,哪是路徑參數(shù)的。例如:/user-Service/user/queryUser/zhangsan/18,這個例子你雖然你看都知道哪個是路徑參數(shù),但畢竟是框架,萬一路徑上有很多的/../../..,還怎么猜?有些規(guī)則該定死,還是要定死的。
  7. 真實(shí)請求到達(dá)網(wǎng)關(guān)后,我們對真實(shí)請求也做了一些處理,即:只保留/服務(wù)名/類路徑/方法路徑的第一個,后續(xù)的路徑均使用一個/**替換掉。舉例:真實(shí)請求為/user-Service/user/queryUser/zhangsan/18,我們處理后為/user-Service/user/queryUser/**。

經(jīng)過上述的規(guī)定和路徑處理后,在下面的代碼中進(jìn)行匹配操作:

oauth2.1鑒權(quán),java,分布式,開發(fā)語言

  • ?methodValue + uri + CacheConstants.FUZZY_PATH即為/user-Service/user/queryUser/**;
  • k.toString()即為/user-Service/user/queryUser/**;
  • 判斷結(jié)果為true。

2.網(wǎng)關(guān)整合Security Oauth2

Gateway網(wǎng)關(guān)是基于webFlux實(shí)現(xiàn)的,所以和一般微服務(wù)整合方式不太一樣。

2.1.認(rèn)證管理器

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

/**
 * @Since: 2023/4/13
 * @Author: Yan
 * @Description Jwt認(rèn)證管理器,對token的真實(shí)性、有效性進(jìn)行校驗(yàn)
 */
@Component
@Slf4j
public class JwtAuthenticationManager implements ReactiveAuthenticationManager {

    @Autowired
    private TokenStore tokenStore;


    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        return Mono.justOrEmpty(authentication)
                .filter(a -> a instanceof BearerTokenAuthenticationToken)
                .cast(BearerTokenAuthenticationToken.class)
                .map(BearerTokenAuthenticationToken::getToken)
                .flatMap((accessToken -> {
                    //解析令牌
                    OAuth2AccessToken oAuth2AccessToken = this.tokenStore.readAccessToken(accessToken);
                    if (oAuth2AccessToken == null) {
                        return Mono.error(new InvalidBearerTokenException("無效的token"));
                    } else if (oAuth2AccessToken.isExpired()) {
                        return Mono.error(new InvalidBearerTokenException("token已過期"));
                    }
                    OAuth2Authentication oAuth2Authentication = this.tokenStore.readAuthentication(accessToken);
                    if (oAuth2Authentication == null) {
                        return Mono.error(new InvalidBearerTokenException("無效的token"));
                    } else {
                        return Mono.just(oAuth2Authentication);
                    }
                }))
                .cast(Authentication.class);
    }
}

2.2..鑒權(quán)管理器

import cn.hutool.core.text.AntPathMatcher;
import cn.hutool.core.text.StrPool;
import com.eden4cloud.common.core.contant.CacheConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @Since: 2023/4/13
 * @Author: Yan
 * @Description Jwt鑒權(quán)管理器:從Redis中獲取所請求的Url所需要的權(quán)限,和用戶token中所攜帶的權(quán)限進(jìn)行比對
 */
@Slf4j
@Component
public class JwtAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    private AntPathMatcher matcher = new AntPathMatcher();

    /**
     * *****當(dāng)前匹配方法要求一個API接口方法上必須要有路徑*****
     *
     * @param mono
     * @param authorizationContext
     * @return
     */
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
        // 處理當(dāng)前請求的uri路徑,最終格式為:/服務(wù)路徑/類路徑/方法上的路徑 /eden-system/user/add
        StringBuilder builder = new StringBuilder();
        String[] split = authorizationContext.getExchange().getRequest().getURI().getPath().split(StrPool.SLASH);
        String uri = builder.append(StrPool.SLASH).append(split[1])//服務(wù)路徑
                .append(StrPool.SLASH).append(split[2])//類路徑
                .append(StrPool.SLASH).append(split[3])//方法路徑
                .toString();
        // 請求方式拼接處理 ,格式為 GET:
        String methodValue = authorizationContext.getExchange().getRequest().getMethodValue() + StrPool.COLON;
        // 獲取所有路徑的權(quán)限列表
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(CacheConstants.OAUTH_PERMS);
        List<String> authorities = new ArrayList<>();
        AtomicBoolean authFlag = new AtomicBoolean(false);
        entries.forEach((k, v) -> {
            // 根據(jù)請求uri路徑,獲取到匹配的緩存權(quán)限數(shù)據(jù)
            if (k.equals(methodValue + uri)
                    || matcher.match(methodValue + uri + CacheConstants.FUZZY_PATH, k.toString())) {
                if (CacheConstants.ANONYMOUS.equals(v.toString())) {
                    // 權(quán)限為**,表示允許匿名訪問
                    authFlag.set(true);
                } else {
                    // 收集當(dāng)前路徑所需的權(quán)限列表
                    authorities.addAll(Arrays.asList((v.toString()).split(StrPool.COMMA)));
                }
            }
        });
        // Collection<? extends GrantedAuthority> authorities1 = mono.block().getAuthorities();


        List<String> finalAuthorities = authorities;
        return mono
                //判斷是否認(rèn)證成功
                .filter(Authentication::isAuthenticated)
                //獲取認(rèn)證后的全部權(quán)限列表
                .flatMapIterable(Authentication::getAuthorities)
                .map(GrantedAuthority::getAuthority)
                //如果包含在url要求的權(quán)限內(nèi),則返回true
                .any(auth -> authFlag.get() || finalAuthorities.contains(auth))
                .map(AuthorizationDecision::new)
                .defaultIfEmpty(new AuthorizationDecision(false))
                ;
    }

}

2.3.security安全配置

import com.eden4cloud.gateway.component.JwtAuthorizationManager;
import com.eden4cloud.gateway.handler.RequestAccessDeniedHandler;
import com.eden4cloud.gateway.handler.RequestAuthenticationEntrypoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.server.resource.web.server.ServerBearerTokenAuthenticationConverter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.web.cors.reactive.CorsWebFilter;

/**
 * @Since: 2023/4/2
 * @Author: Yan
 * @Description security安全配置
 */
@Configuration
@EnableWebFluxSecurity
public class EdenGatewayWebSecurityConfig {

    @Autowired
    private JwtAuthorizationManager jwtAuthorizationManager;

    @Autowired
    private ReactiveAuthenticationManager authenticationManager;

    @Autowired
    private RequestAuthenticationEntrypoint requestAuthenticationEntrypoint;

    @Autowired
    private RequestAccessDeniedHandler requestAccessDeniedHandler;

    @Autowired
    private CorsWebFilter corsWebFilter;

    // @Autowired
    // private GlobalAuthenticationFilter authenticationFilter;

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(authenticationManager);
        authenticationWebFilter.setServerAuthenticationConverter(new ServerBearerTokenAuthenticationConverter());

        http
                .csrf().disable()
                .authorizeExchange()
                //對oauth的端點(diǎn)進(jìn)行放行
                .pathMatchers("/eden-oauth/oauth/**").permitAll()
                //其他請求必須鑒權(quán),使用鑒權(quán)管理器
                .anyExchange().access(jwtAuthorizationManager)
                .and()
                //鑒權(quán)異常處理
                .exceptionHandling()
                .authenticationEntryPoint(requestAuthenticationEntrypoint)
                .accessDeniedHandler(requestAccessDeniedHandler)
                .and()
                //跨域過濾器
                .addFilterAt(corsWebFilter, SecurityWebFiltersOrder.CORS)
                //token認(rèn)證過濾器
                .addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                // .addFilterAfter(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION)
        ;
        return http.build();
    }
}

2.4.將網(wǎng)關(guān)作為資源服務(wù)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

/**
 * @Author: Yan
 * @Since: 2023/2/4
 * @Description: 資源服務(wù)器解析鑒權(quán)配置類
 */
@Configuration
public class EdenGatewayResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    private TokenStore tokenStore;

    //公鑰
    private static final String RESOURCE_ID = "eden-gateway";

    /**
     * Http安全配置,對每個到達(dá)系統(tǒng)的http請求鏈接進(jìn)行校驗(yàn)
     *
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/**").permitAll();
    }

    /**
     * 資源服務(wù)的安全配置
     *
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources
                .resourceId(RESOURCE_ID)
                .tokenStore(tokenStore)
                .stateless(true);
    }
}

2.5.其他配置

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

/**
 * @CreateTime: 2023-01-2023/1/11 15:09
 * @Author: Yan
 * @Description  注冊網(wǎng)關(guān)過濾器示例
 */
@Configuration
@AutoConfigureAfter(GatewayAutoConfiguration.class)
public class GatewayRoutesConfiguration {

    /**
     * 跨域配置
     *
     * @return
     */
    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }

    // @Bean(name = "ipKeyResolver")
    // public KeyResolver userIpKeyResolver() {
    //     return new IpKeyResolver();
    // }
    //
    // @Bean
    // public RouteLocator routeLocator(RouteLocatorBuilder builder) {
    //     StripPrefixGatewayFilterFactory filterFactory = new StripPrefixGatewayFilterFactory();
    //     StripPrefixGatewayFilterFactory.Config partsConfig = filterFactory.newConfig();
    //     partsConfig.setParts(1);
    //
    //     MyGatewayFilterFactory factory = new MyGatewayFilterFactory();
    //     MyGatewayFilterFactory.PathsConfig pathsConfig = factory.newConfig();
    //     pathsConfig.setPaths(Arrays.asList("/AAA", "/BBB"));
    //
    //     return builder.routes()
    //             .route(r -> r.path("/life/**")
    //                     .uri("lb://eden-life")
    //                     .filters(factory.apply(pathsConfig), filterFactory.apply(partsConfig))
    //                     .id("eden-life"))
    //             .build();
    // }
}

2.6.自定義鑒權(quán)過濾器工廠

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.eden4cloud.common.security.exception.InvalidTokenException;
import com.eden4cloud.common.security.utils.JwtUtils;
import com.eden4cloud.common.util.sign.Base64;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @CreateTime: 2023-01-2023/1/11 9:34
 * @Author: Yan
 * @Description 自定義鑒權(quán)過濾器工廠:設(shè)置訪問白名單;重新封裝鑒權(quán)認(rèn)證通過的請求頭;
 */
//1. 編寫實(shí)現(xiàn)類繼承AbstractGatewayFilterFactory抽象類
@Component
public class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthGatewayFilterFactory.IgnoreUrlsConfig> {//4. 指定泛型,靜態(tài)的內(nèi)部實(shí)體類

    @Autowired
    private TokenStore tokenStore;

    //5. 重寫無參構(gòu)造方法,指定內(nèi)部實(shí)體類接收參數(shù)
    public AuthGatewayFilterFactory() {
        super(IgnoreUrlsConfig.class);
    }

    @Override
    public GatewayFilter apply(IgnoreUrlsConfig config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            //直接放行部分請求路徑,如登錄、退出等  需要排除的路徑弄成可yaml配置的
            boolean flag = config.ignoreUrls.contains(request.getURI().getPath())
                    || config.ignoreUrls.stream().filter(i -> i.endsWith("/**"))
                    .anyMatch(i -> exchange.getRequest().getURI().getPath().startsWith(i.replace("**", "")));
            if (flag) {
                return chain.filter(exchange);
            }
            //重新封裝新的請求中數(shù)據(jù)
            ServerWebExchange webExchange = rebuildRequestHeaders(exchange, request);
            if (webExchange == null) {
                return Mono.error(new InvalidTokenException());
            }
            return chain.filter(webExchange);
        };
    }

    /**
     * 重新封裝新的請求數(shù)據(jù)
     *
     * @param exchange
     * @param request
     * @return
     */
    private ServerWebExchange rebuildRequestHeaders(ServerWebExchange exchange, ServerHttpRequest request) {
        //獲取請求頭中的令牌
        String token = JwtUtils.getToken(request);
        if (StrUtil.isBlank(token)) {
            return null;
        }

        OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token);
        Map<String, Object> additionalInformation = oAuth2AccessToken.getAdditionalInformation();
        List<String> authorities = (List<String>) additionalInformation.get("authorities");
        //獲取用戶名
        String username = additionalInformation.get("user_name").toString();
        //獲取用戶權(quán)限
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("username", username);
        jsonObject.put("authorities", authorities);
        //將解析后的token加密后重新放入請求頭,方便后續(xù)微服務(wù)解析獲取用戶信息
        String base64 = Base64.encode(jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8));
        request = exchange.getRequest().mutate().header("token", base64).build();
        exchange.mutate().request(request);
        return exchange;
    }

    /**
     * 6. 重寫shortcutFieldOrder()指定接收參數(shù)的字段順序
     * Returns hints about the number of args and the order for shortcut parsing.
     *
     * @return the list of hints
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("ignoreUrls");
    }

    /**
     * 7. 重寫shortcutType()指定接收參數(shù)的字段類型
     */
    @Override
    public ShortcutType shortcutType() {
        return ShortcutType.GATHER_LIST;
    }

    /**
     * 3. 定義匿名內(nèi)部實(shí)體類,定義接收參數(shù)的字段
     */
    @Data
    public static class IgnoreUrlsConfig {
        //傳遞多個參數(shù)
        private List<String> ignoreUrls;
    }
}

2.7.網(wǎng)關(guān)路由規(guī)則

#路由配置
spring:
  cloud:
    gateway:
      routes:
        - id: eden-life
          uri: lb://eden-life
          predicates:
            - Path=/eden-life/**
          filters:
            - StripPrefix=1
            - name: Auth
              args:
                ignoreUrls:
                  - /auth-server/login
                  - /oauth/**
                  - /life/**

2.6和2.7主要是展示配置了動態(tài)的請求白名單功能?;趎acos的配置中心功能,可以實(shí)現(xiàn)動態(tài)刷新,白名單設(shè)置實(shí)時生效。

2.8.異常處理文章來源地址http://www.zghlxwxcb.cn/news/detail-770505.html

import cn.hutool.json.JSONUtil;
import com.eden4cloud.common.core.entity.R;
import com.eden4cloud.common.security.exception.SecurityExceptionEnum;
import org.apache.http.HttpHeaders;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

/**
 * @Since: 2023/4/17
 * @Author: Yan
 * @Description TODO
 */
@Component
public class RequestAccessDeniedHandler implements ServerAccessDeniedHandler {

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException e) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.OK);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        DataBuffer buffer = response.bufferFactory()
                .wrap(JSONUtil.toJsonStr(R.error(SecurityExceptionEnum.NO_PERMISSION.getMsg())).getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(buffer));
    }
}
import cn.hutool.json.JSONUtil;
import com.eden4cloud.common.core.entity.R;
import com.eden4cloud.common.security.exception.SecurityExceptionEnum;
import org.apache.http.HttpHeaders;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

/**
 * @Since: 2023/4/17
 * @Author: Yan
 * @Description TODO
 */
@Component
public class RequestAuthenticationEntrypoint implements ServerAuthenticationEntryPoint {

    @Override
    public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.OK);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        DataBuffer buffer = response.bufferFactory()
                .wrap(JSONUtil.toJsonStr(R.error(SecurityExceptionEnum.INVALID_TOKEN.getMsg())).getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(buffer));
    }
}

到了這里,關(guān)于Spring Gateway + Oauth2 + Jwt網(wǎng)關(guān)統(tǒng)一鑒權(quán)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Spring Gateway+Security+OAuth2+RBAC 實(shí)現(xiàn)SSO統(tǒng)一認(rèn)證平臺

    Spring Gateway+Security+OAuth2+RBAC 實(shí)現(xiàn)SSO統(tǒng)一認(rèn)證平臺

    背景:新項(xiàng)目準(zhǔn)備用SSO來整合之前多個項(xiàng)目的登錄和權(quán)限,同時引入網(wǎng)關(guān)來做后續(xù)的服務(wù)限流之類的操作,所以搭建了下面這個系統(tǒng)雛形。 : Spring Gateway, Spring Security, JWT, OAuth2, Nacos, Redis, Danymic datasource, Javax,?thymeleaf 如果對上面這些技術(shù)感興趣,可以繼續(xù)往下閱讀 如

    2024年02月13日
    瀏覽(25)
  • 微服務(wù)鑒權(quán)中心之網(wǎng)關(guān)配置SpringSecurity+oauth2

    微服務(wù)鑒權(quán)中心之網(wǎng)關(guān)配置SpringSecurity+oauth2

    微服務(wù)鑒權(quán)中心流程如下: 1. 網(wǎng)關(guān)配置oauth2之 TokenStore存儲方式,此處用 RedisTokenStore 2.網(wǎng)關(guān)配置security 3.網(wǎng)關(guān)攔截token

    2024年02月14日
    瀏覽(24)
  • 權(quán)限認(rèn)證SpringCloud GateWay、SpringSecurity、OAuth2.0、JWT一網(wǎng)打盡!

    權(quán)限認(rèn)證SpringCloud GateWay、SpringSecurity、OAuth2.0、JWT一網(wǎng)打盡!

    1.它是如何工作的? ? 客戶端向 Spring Cloud Gateway 發(fā)出請求。如果Gateway處理程序映射確定一個請求與路由相匹配,它將被發(fā)送到Gateway Web處理程序。這個處理程序通過一個特定于該請求的過濾器鏈來運(yùn)行該請求。過濾器被虛線分割的原因是,過濾器可以在代理請求發(fā)送之前和

    2024年04月08日
    瀏覽(23)
  • spring-security -oauth2 整合 JWT

    spring-security -oauth2 整合 JWT

    在這個基礎(chǔ)上,進(jìn)行整合。 spring security oauth2學(xué)習(xí) -- 快速入門_本郡主是喵的博客-CSDN博客 先把? reids,common-pools? 等依賴刪掉。 刪掉redis的下相關(guān)配置 1.1?導(dǎo)入依賴 1.2 核心代碼 創(chuàng)建 jwtTokenConfig.java 在 AuthenticationServer.java 里面新增這些。 ?運(yùn)行,啟動! ?復(fù)制這個token去官網(wǎng)解析

    2024年02月09日
    瀏覽(23)
  • 基于SpringCloud + Oauth2.0 + ShiroRedis + JWT + Gateway + Nacos + Nginx + Vue實(shí)現(xiàn)的SaaS數(shù)字商城系統(tǒng)

    基于SpringCloud + Oauth2.0 + ShiroRedis + JWT + Gateway + Nacos + Nginx + Vue實(shí)現(xiàn)的SaaS數(shù)字商城系統(tǒng)

    SaaS的英文全稱是Software as a Service,意思是軟件即服務(wù) ,是云計算的其中一種服務(wù)模式 SaaS是一種通過Internet提供集中托管應(yīng)用程序的方式,企業(yè)用戶一般通過客戶端或網(wǎng)頁來使用,無需購買、安裝或維護(hù)任何軟件及硬件,因此 SaaS應(yīng)用程序又被稱為\\\"基于Web的軟件\\\" 或 \\\"托管軟件

    2024年01月20日
    瀏覽(24)
  • SpringSecurity+Oauth2+JWT

    快速入門 1. 創(chuàng)建基礎(chǔ)項(xiàng)目 file == new == project == Spring Initializr ==next == web(Spring Web)、Security(Spring Security) ==一直下一步 2. 編寫代碼進(jìn)行測試 創(chuàng)建controller static下創(chuàng)建login.html、main.html 3. 啟動項(xiàng)目進(jìn)行測試 訪問http://localhost:8080/login.html 會進(jìn)入SpringSecurity框架自帶的登入頁面 用戶默認(rèn)

    2024年02月12日
    瀏覽(24)
  • JWT和OAuth2.0

    JWT和OAuth2.0

    JWT和OAuth2.0沒有可比性,是兩個完全不同的東西。 JWT是一種認(rèn)證協(xié)議,提供了一種用于發(fā)布接入令牌(Access Token),并對發(fā)布的簽名接入令牌進(jìn)行驗(yàn)證的方法。SSO私鑰加密token。應(yīng)用端公鑰解密token, OAuth2.0是一種授權(quán)框架,提供了一套詳細(xì)的授權(quán)機(jī)制(指導(dǎo))。用戶或應(yīng)用可以通

    2023年04月16日
    瀏覽(16)
  • Spring Security Oauth2.1 最新版 1.1.0 整合 gateway 完成授權(quán)認(rèn)證(擁抱 springboot 3.1)

    Spring Security Oauth2.1 最新版 1.1.0 整合 gateway 完成授權(quán)認(rèn)證(擁抱 springboot 3.1)

    目錄 背景 demo地址 版本 Spring Boot 3.1 Spring Authorization Server 1.1.0 基礎(chǔ) spring security OAuth2 模塊構(gòu)成 授權(quán)方式 認(rèn)證方式 集成過程 官方demo 代碼集成 依賴 授權(quán)服務(wù)AuthorizationServerConfig配置 重要組件 測試 查看授權(quán)服務(wù)配置 訪問授權(quán)服務(wù) 授權(quán) 回調(diào) 獲取?access_token 獲取用戶信息 個性

    2024年02月08日
    瀏覽(19)
  • Spring Security Oauth2.1 最新版 1.1.0 整合 (基于 springboot 3.1.0)gateway 完成授權(quán)認(rèn)證

    Spring Security Oauth2.1 最新版 1.1.0 整合 (基于 springboot 3.1.0)gateway 完成授權(quán)認(rèn)證

    目錄 背景 demo地址 版本 Spring Boot 3.1 Spring Authorization Server 1.1.0 基礎(chǔ) spring security OAuth2 模塊構(gòu)成 授權(quán)方式 認(rèn)證方式 集成過程 官方demo 代碼集成 依賴 授權(quán)服務(wù)AuthorizationServerConfig配置 重要組件 測試 查看授權(quán)服務(wù)配置 訪問授權(quán)服務(wù) 授權(quán) 回調(diào) 獲取?access_token 獲取用戶信息 個性

    2024年02月11日
    瀏覽(24)
  • SpringSecurity+ Oauth2.0+JWT 0-1

    SpringSecurity+ Oauth2.0+JWT 0-1

    AuthorizationServer 需要繼承AuthorizationServerConfigurerAdapter AuthorizationServerConfigurerAdapter源碼 AuthorizationServerSecurityConfigurer:配置令牌端點(diǎn)(Token Endpoint)的安全約束 ClientDetailsServiceConfigurer:配置OAuth2客戶端 AuthorizationServerEndpointsConfigurer:配置授權(quán)(authorization)以及令牌(token)的訪

    2024年02月07日
    瀏覽(21)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包