精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Spring Gateway、Sa-Token、Nacos 認證/鑒權方案,yyds!

開發 前端
Spring Security太重而且配置繁瑣。自己實現所有的點必須又要顧及到,更是麻煩。最近看到一個權限認證框架,真是夠簡單高效。這里分享一個使用Sa-Token的gateway鑒權demo。

之前進行鑒權、授權都要寫一大堆代碼。如果使用像Spring Security這樣的框架,又要花好多時間學習,拿過來一用,好多配置項也不知道是干嘛用的,又不想了解。要是不用Spring Security,token的生成、校驗、刷新,權限的驗證分配,又全要自己寫,想想都頭大。

Spring Security太重而且配置繁瑣。自己實現所有的點必須又要顧及到,更是麻煩。

最近看到一個權限認證框架,真是夠簡單高效。這里分享一個使用Sa-Token的gateway鑒權demo。

需求分析

圖片圖片

結構

圖片圖片

認證

sa-token模塊

我們首先編寫sa-token模塊進行token生成和權限分配。

在sa-token的session模式下生成token非常方便,只需要調用

StpUtil.login(Object id);

就可以為賬號生成 Token 憑證與 Session 會話了。

配置信息

server:
  # 端口
port:8081

spring:
application:
    name:weishuang-account
datasource:
    driver-class-name:com.mysql.cj.jdbc.Driver
    url:jdbc:mysql://127.0.0.1:3306/weishuang_account?useUnicode=true&characterEncoding=utf8&autoRecnotallow=true&allowMultiQueries=true&useSSL=false&serverTimeznotallow=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL
    username:root
    password:root
# redis配置
redis:
    # Redis數據庫索引(默認為0)
    database:0
    # Redis服務器地址
    host:127.0.0.1
    # Redis服務器連接端口
    port:6379
    # Redis服務器連接密碼(默認為空)
    # password:
    # 連接超時時間
    timeout:10s
    lettuce:
      pool:
        # 連接池最大連接數
        max-active:200
        # 連接池最大阻塞等待時間(使用負值表示沒有限制)
        max-wait:-1ms
        # 連接池中的最大空閑連接
        max-idle:10
        # 連接池中的最小空閑連接
        min-idle:0


############## Sa-Token 配置 (文檔: https://sa-token.cc) ##############
sa-token:
# token名稱 (同時也是cookie名稱)
token-name:weishuang-token
# token有效期,單位s 默認30天, -1代表永不過期
timeout:2592000
# token臨時有效期 (指定時間內無操作就視為token過期) 單位: 秒
activity-timeout:-1
# 是否允許同一賬號并發登錄 (為true時允許一起登錄, 為false時新登錄擠掉舊登錄)
is-concurrent:true
# 在多人登錄同一賬號時,是否共用一個token (為true時所有登錄共用一個token, 為false時每次登錄新建一個token)
is-share:true
# token風格
token-style:uuid
# 是否輸出操作日志
is-log:false
# token前綴
token-prefix:Bearer

在sa-token的配置中,我使用了token-name來指定token的名稱,如果不指定那么就是默認的satoken。

使用token-prefix來指定token的前綴,這樣前端在header里傳入token的時候就要加上Bearer了(注意有個空格),建議和前端商量一下需不需要這個前綴,如果不使用,直接傳token就好了。

現在調用接口時傳入的格式就是

weishuang-token = Bearer token123456

sa-token的session模式需要redis來存儲session,在微服務中,各個服務的session也需要redis來同步。

當然sa-token也支持jwt來生成無狀態的token,這樣就不需要在服務中引入redis了。本文使用session模式(jwt的刷新token等機制還要自己實現,session的刷新sa-token都幫我們做好了,使用默認的模式更加方便,而且功能更多)

我們來編寫一個登錄接口

User
@Data
public class User {

    /**
     * id
     */
    private String id;

    /**
     * 賬號
     */
    private String userName;

    /**
     * 密碼
     */
    private String password;

}
UserController
@RestController
@RequestMapping("/account/user/")
public class UserController {

    @Autowired
    private UserManager userManager;

    @PostMapping("doLogin")
    public SaResult doLogin(@RequestBody AccountUserLoginDTO req) {
        userManager.login(req);

        return SaResult.ok("登錄成功");
    }
}
UserManager
@Component
publicclass UserManagerImpl implements UserManager {

    @Autowired
    private UserService userService;

    @Override
    public void login(AccountUserLoginDTO req) {
        //生成密碼
        String password = PasswordUtil.generatePassword(req.getPassword());
        //調用數據庫校驗是否存在用戶
        User user = userService.getOne(req.getUserName(), password);
        if (user == null) {
            thrownew RuntimeException("賬號或密碼錯誤");
        }
        
        //為賬號生成Token憑證與Session會話
        StpUtil.login(user.getId());
        //為該用戶的session存儲更多信息
        //這里為了方便直接把user實體存進去了,也包括了密碼,自己實現時不建議這樣做。
        StpUtil.getSession().set("USER_DATA", user);
    }

}
UserService
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserMapper userMapper;

    public User getOne(String username, String password){
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserName,username)
                .eq(User::getPassword,password);

        return userMapper.selectOne(queryWrapper);
    }
}

gateway模塊

依賴

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

        <!-- 引入gateway網關 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- Sa-Token 權限認證(Reactor響應式集成), 在線文檔:https://sa-token.cc -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-reactor-spring-boot-starter</artifactId>
            <version>1.34.0</version>
        </dependency>

        <!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-dao-redis-jackson</artifactId>
            <version>1.34.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

    </dependencies>

配置

server:
  port:9000
spring:
application:
    name:weishuang-gateway
cloud:
    loadbalancer:
      ribbon:
        enabled:false
    nacos:
      discovery:
        username:nacos
        password:nacos
        server-addr:localhost:8848
    gateway:
      routes:
        -id:account
          uri:lb://weishuang-account
          order:1
          predicates:
            -Path=/account/**
# redis配置
redis:
    # Redis數據庫索引(默認為0)
    database:0
    # Redis服務器地址
    host:127.0.0.1
    # Redis服務器連接端口
    port:6379
    # Redis服務器連接密碼(默認為空)
    # password:
    # 連接超時時間
    timeout:10s
    lettuce:
      pool:
        # 連接池最大連接數
        max-active:200
        # 連接池最大阻塞等待時間(使用負值表示沒有限制)
        max-wait:-1ms
        # 連接池中的最大空閑連接
        max-idle:10
        # 連接池中的最小空閑連接
        min-idle:0

############## Sa-Token 配置 (文檔: https://sa-token.cc) ##############
sa-token:
# token名稱 (同時也是cookie名稱)
token-name:weishuang-token
# token有效期,單位s 默認30天, -1代表永不過期
timeout:2592000
# token臨時有效期 (指定時間內無操作就視為token過期) 單位: 秒
activity-timeout:-1
# 是否允許同一賬號并發登錄 (為true時允許一起登錄, 為false時新登錄擠掉舊登錄)
is-concurrent:true
# 在多人登錄同一賬號時,是否共用一個token (為true時所有登錄共用一個token, 為false時每次登錄新建一個token)
is-share:true
# token風格
token-style:uuid
# 是否輸出操作日志
is-log:false
# token前綴
token-prefix:Bearer

同樣的,在gateway中也需要配置sa-token和redis,注意和在account服務中配置的要一致,否則在redis中獲取信息的時候找不到。

gateway我們也注冊到nacos中。

攔截認證

package com.weishuang.gateway.gateway.config;

import cn.dev33.satoken.reactor.filter.SaReactorFilter;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Configuration
publicclass SaTokenConfigure {

    // 注冊 Sa-Token全局過濾器
    @Bean
    public SaReactorFilter getSaReactorFilter() {
        returnnew SaReactorFilter()
                // 攔截地址
                .addInclude("/**")    /* 攔截全部path */
                // 開放地址
                .addExclude("/favicon.ico")
                // 鑒權方法:每次訪問進入
                .setAuth(obj -> {
                    // 登錄校驗 -- 攔截所有路由,并排除/account/user/doLogin用于開放登錄
                    SaRouter.match("/**", "/account/user/doLogin", r -> StpUtil.checkLogin());

//                    // 權限認證 -- 不同模塊, 校驗不同權限
//                    SaRouter.match("/account/**", r -> StpUtil.checkRole("user"));
//                    SaRouter.match("/admin/**", r -> StpUtil.checkRole("admin"));
//                    SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
//                    SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));

                    // 更多匹配 ...  */
                })
                // 異常處理方法:每次setAuth函數出現異常時進入
                .setError(e -> {
                    return SaResult.error(e.getMessage());
                })
                ;
    }
}

只需要在gateway中添加一個全局過濾器進行鑒權操作就可以實現認證/鑒權操作了。

這里我們對**全部路徑進行攔截,但不要忘記把我們的登錄接口釋放出來,允許訪問。

到這里簡單的認證操作就實現了。我們僅僅使用了sa-token的一個StpUtil.login(Object id)方法,其他事情sa-token都幫我們完成了,更無需復雜的配置和多到爆炸的Bean。

鑒權

有時候一個token認證并不能讓我們區分用戶能不能訪問這個資源,使用那個菜單,我們需要更細粒度的鑒權。

在經典的RBAC模型里,用戶會擁有多個角色,不同的角色又會有不同的權限。

這里我們使用五個表來表示用戶、角色、權限之間的關系。

圖片圖片

 很顯然,我們想判斷用戶有沒有權限訪問一個path,需要判斷用戶是否還有該權限。

在sa-token中想要實現這個功能,只需要實現StpInterface接口即可。

/**
 * 自定義權限驗證接口擴展 
 */
@Component   
publicclass StpInterfaceImpl implements StpInterface {

    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        // 返回此 loginId 擁有的權限列表 
        return ...;
    }

    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        // 返回此 loginId 擁有的角色列表
        return ...;
    }

}

我們在gateway實現這個接口,為用戶賦予權限,再進行權限校驗,就可以精確到path了。

我們使用先從Redis中獲取緩存數據,獲取不到時走RPC調用account服務獲取。

為了更方便的使用gateway調用account服務,我們使用nacos進行服務發現,用feign調用。

在account和gateway服務中配置nacos

配置nacos

在兩個服務中加入nacos的配置

spring:
  cloud:
    nacos:
      discovery:
        username:nacos
        password:nacos
        server-addr:localhost:8848
datasource:
    driver-class-name:com.mysql.cj.jdbc.Driver
    url:jdbc:mysql://127.0.0.1:3306/weishuang_account?useUnicode=true&characterEncoding=utf8&autoRecnotallow=true&allowMultiQueries=true&useSSL=false&serverTimeznotallow=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL
    username:root
    password:root

配置gateway

需要注意的是,gateway是基于WebFlux的一個響應式組件,HttpMessageConverters不會像Spring Mvc一樣自動注入,需要我們手動配置。

package com.weishuang.gateway.gateway.config;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;

import java.util.stream.Collectors;

@Configuration
publicclass HttpMessageConvertersConfigure {
    @Bean
    @ConditionalOnMissingBean
    public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
        returnnew HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
    }
}

實現獲取角色、權限接口

在account中實現通過用戶獲取角色、獲取權限的接口

RoleController、PermissionController
@RestController
@RequestMapping("/account/role/")
publicclass RoleController {

    @Autowired
    private RoleManager roleManager;

    @PostMapping("/getRoles")
    public List<RoleDTO> getRoles(@RequestParam String userId) {
        return roleManager.getRoles(userId);
    }
}

@RestController
@RequestMapping("/account/permission/")
publicclass PermissionController {

    @Autowired
    private PermissionManager permissionManager;

    @PostMapping("/getPermissions")
    public List<PermissionDTO> getPermissions(@RequestParam String userId) {
        return permissionManager.getPermissions(userId);
    }

}
RoleManager
@Component
publicclass RoleManagerImpl implements RoleManager {

    @Autowired
    private RoleService roleService;

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private Role2RoleDTOCovert role2RoleDTOCovert;

    @Override
    public List<RoleDTO> getRoles(String userId) {
        List<UserRole> userRoles = userRoleService.getByUserId(userId);
        Set<String> roleIds = userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toSet());
        List<RoleDTO> roleDTOS = role2RoleDTOCovert.covertTargetList2SourceList(roleService.getByIds(roleIds));

        //服務不對外暴露,網關不傳token到子服務,這里通過userId獲取session,并設置角色。
        String tokenValue = StpUtil.getTokenValueByLoginId(userId);

        //為這個token在redis中設置角色,使網關獲取更方便
        if(StringUtils.isNotEmpty(tokenValue)){
            if(CollectionUtils.isEmpty(roleDTOS)){
                StpUtil.getTokenSessionByToken(tokenValue).set("ROLES", "");
            }else{
                List<String> roleNames = roleDTOS.stream().map(RoleDTO::getRoleName).collect(Collectors.toList());
                StpUtil.getTokenSessionByToken(tokenValue).set("ROLES", ListUtil.list2String(roleNames));
            }
        }
        return roleDTOS;
    }
}
PermissionManager
@Component
publicclass PermissionManagerImpl implements PermissionManager {

    @Autowired
    private PermissionService permissionService;

    @Autowired
    private RolePermService rolePermService;

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private Permission2PermissionDTOCovert permissionDTOCovert;

    @Override
    public List<PermissionDTO> getPermissions(String userId) {

        //獲取用戶的角色
        List<UserRole> roles = userRoleService.getByUserId(userId);
        if (CollectionUtils.isEmpty(roles)) {
            handleUserPermSession(userId, null);
        }

        Set<String> roleIds = roles.stream().map(UserRole::getRoleId).collect(Collectors.toSet());

        List<RolePerm> rolePerms = rolePermService.getByRoleIds(roleIds);

        if (CollectionUtils.isEmpty(rolePerms)) {
            handleUserPermSession(userId, null);
        }

        Set<String> permIds = rolePerms.stream().map(RolePerm::getPermId).collect(Collectors.toSet());
        List<PermissionDTO> perms = permissionDTOCovert.covertTargetList2SourceList(permissionService.getByIds(permIds));

        handleUserPermSession(userId, perms);
        return perms;
    }


    private void handleUserPermSession(String userId, List<PermissionDTO> perms) {
        //通過userId獲取session,并設置權限
        String tokenValue = StpUtil.getTokenValueByLoginId(userId);

        if (StringUtils.isNotEmpty(tokenValue)) {
            //為了防止沒有權限的用戶多次進入到該接口,沒權限的用戶在redis中存入空字符串
            if (CollectionUtils.isEmpty(perms)) {
                StpUtil.getTokenSessionByToken(tokenValue).set("PERMS", "");
            } else {
                List<String> paths = perms.stream().map(PermissionDTO::getPath).collect(Collectors.toList());
                StpUtil.getTokenSessionByToken(tokenValue).set("PERMS", ListUtil.list2String(paths));
            }
        }
    }
}

gateway獲取角色、權限

方式一:

官方寫的實現StpInterfaceImpl中的方法

作為一個異步組件,gateway中不允許使用引起阻塞的同步調用,若使用feign進行調用就會發生錯誤,我們使用CompletableFuture來將同步調用轉換成異步操作,但使用CompletableFuture我們需要指定線程池,否則將會使用默認的ForkJoinPool

這里我們創建一個線程池,用于權限獲取使用

package com.weishuang.gateway.gateway.config;

import org.springframework.context.annotation.Configuration;

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

@Configuration
publicclass ThreadPollConfig {

    privatefinal BlockingQueue<Runnable> asyncSenderThreadPoolQueue = new LinkedBlockingQueue<Runnable>(50000);

    publicfinal ExecutorService USER_ROLE_PERM_THREAD_POOL = new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors(),
            Runtime.getRuntime().availableProcessors(),
            1000 * 60,
            TimeUnit.MILLISECONDS,
            this.asyncSenderThreadPoolQueue,
            new ThreadFactory() {
                privatefinal AtomicInteger threadIndex = new AtomicInteger(0);

                @Override
                public Thread newThread(Runnable r) {
                    returnnew Thread(r, "RolePermExecutor_" + this.threadIndex.incrementAndGet());
                }
            });

}
StpInterfaceImpl
@Component
publicclass StpInterfaceImpl implements StpInterface {

    @Autowired
    private RoleFacade roleFacade;

    @Autowired
    private PermissionFacade permissionFacade;

    @Autowired
    private ThreadPollConfig threadPollConfig;

    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        Object res = StpUtil.getTokenSession().get("PERMS");
        if (res == null) {
            CompletableFuture<List<String>> permFuture = CompletableFuture.supplyAsync(() -> {
                // 返回此 loginId 擁有的權限列表
                List<PermissionDTO> permissions = permissionFacade.getPermissions((String) loginId);

                return permissions.stream().map(PermissionDTO::getPath).collect(Collectors.toList());
            }, threadPollConfig.USER_ROLE_PERM_THREAD_POOL);
            try {
                return permFuture.get();
            } catch (InterruptedException | ExecutionException e) {
                thrownew RuntimeException(e);
            }
        }
        String paths = (String) res;
        System.out.println(paths);
        return ListUtil.string2List(paths);
    }

    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        Object res = StpUtil.getTokenSession().get("ROLES");
        if (res == null) {
            CompletableFuture<List<String>> roleFuture = CompletableFuture.supplyAsync(() -> {
                // 返回此 loginId 擁有的權限列表
                List<RoleDTO> roles = roleFacade.getRoles((String) loginId);

                return roles.stream().map(RoleDTO::getRoleName).collect(Collectors.toList());
            }, threadPollConfig.USER_ROLE_PERM_THREAD_POOL);
            try {
                return roleFuture.get();
            } catch (InterruptedException | ExecutionException e) {
                thrownew RuntimeException(e);
            }
        }
        String roleNames = (String) res;
        System.out.println(roleNames);
        return ListUtil.string2List(roleNames);
    }

}
gateway配置過濾器,實現鑒權
@Component
publicclass ForwardAuthFilter implements WebFilter {
    static Set<String> whitePaths = new HashSet<>();


    static {
        whitePaths.add("/account/user/doLogin");
        whitePaths.add("/account/user/logout");
        whitePaths.add("/account/user/register");
    }

    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {

        ServerHttpRequest serverHttpRequest = serverWebExchange.getRequest();


        String path = serverHttpRequest.getPath().toString();

        //需要校驗權限
        if(!whitePaths.contains(path)){

            //判斷用戶是否有該權限
            if(!StpUtil.hasPermission(path)){
                thrownew NotPermissionException(path);
            }
        }

        return webFilterChain.filter(serverWebExchange);
    }
}

方式二:

如果您覺得一定要使用響應式才行,那么無需實現StpInterfaceImpl

/**
 * 全局過濾器
 */
@Component
publicclass ForwardAuthFilter implements WebFilter {

    @Autowired
    private WebClient.Builder webClientBuilder;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        String path = serverHttpRequest.getPath().toString();

        // /api開頭的都要鑒權
        if (StringUtils.isNotEmpty(path) && path.startsWith("/api")) {
            Mono<List<String>> permissionList = getPermissionList();

            return permissionList.flatMap(list -> {
                if (!StpUtil.stpLogic.hasElement(list, path)) {
                    return Mono.error(new NotPermissionException(path));
                }
                return chain.filter(exchange);
            });
        }
        return chain.filter(exchange);
    }

    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {
        return WebClient.builder();
    }

    private Mono<List<String>> getPermissionList() {
        String userId = (String) StpUtil.getLoginId();

        Mono<List<PermissionDTO>> listMono = webClientBuilder.build()
                .post()
                .uri("http://weishuang-account/account/permission/getPermissions")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .body(BodyInserters.fromFormData("userId", userId))
                .retrieve()
                .bodyToFlux(PermissionDTO.class)
                .collectList();

        return listMono.map(permissions -> permissions.stream().map(PermissionDTO::getPath).collect(Collectors.toList()));
    }

}
修改sa-token的配置
@Configuration
publicclass SaTokenConfigure {

    // 注冊 Sa-Token全局過濾器
    @Bean
    public SaReactorFilter getSaReactorFilter() {
        returnnew SaReactorFilter()
                // 攔截地址
                .addInclude("/**")    /* 攔截全部path */
                // 開放地址
                .addExclude("/favicon.ico")
                // 鑒權方法:每次訪問進入
                .setAuth(obj -> {
                    // 登錄校驗 -- 攔截所有路由,排除白名單
                    SaRouter.match("/**")
                            .notMatch(new ArrayList<>(WhitePath.whitePaths))
                            .check(r -> StpUtil.checkLogin());
                })
                // 異常處理方法:每次setAuth函數出現異常時進入
                .setError(e -> {
                    return SaResult.error(e.getMessage());
                });
    }
}
白名單
public class WhitePath {

    static Set<String> whitePaths = new HashSet<>();

    static {
        whitePaths.add("/account/user/doLogin");
        whitePaths.add("/account/user/logout");
        whitePaths.add("/account/user/register");
    }
}
責任編輯:武曉燕 來源: 碼猿技術專欄
相關推薦

2025-09-15 09:34:45

2025-10-09 00:00:15

2023-11-28 17:24:45

2022-02-18 08:34:33

JavaSa-Token項目

2021-04-13 14:47:53

認證授權Java

2025-08-01 09:25:30

2019-05-20 14:57:35

Tomcat容器安全

2024-10-14 11:56:50

2025-07-30 09:15:22

2024-05-28 08:24:18

2024-10-10 12:21:56

JWTSession擴展性

2023-03-30 07:48:46

接口鑒權SpringBoot

2021-03-03 13:25:35

CookieSessionToken

2024-08-30 09:27:35

2021-05-27 07:12:19

單點登錄系統

2025-02-26 13:00:00

SpringBootJava開發

2023-02-28 08:57:06

Spring上下線緩存

2021-09-01 10:15:15

前端cookiesession

2021-09-02 07:00:32

鑒權Web 應用Cookie-sess

2025-02-27 13:00:00

SpringBoot數據鑒權代碼
點贊
收藏

51CTO技術棧公眾號

亚洲激情啪啪| 2019中文字幕在线免费观看| 亚洲三级在线观看视频| 日本成人不卡| 久久久www成人免费毛片麻豆| 国产精品久久久久久久天堂| 国产一二三四区| 欧美jizz19性欧美| 欧美电影一区二区三区| 99精品在线免费视频| 在线观看a视频| 高清不卡一区二区在线| 国产精品99久久久久久人 | 99久久国产综合精品麻豆| 日av在线播放中文不卡| 久久免费看少妇高潮v片特黄| 欧美一区二区三区久久| 欧美美女黄视频| 超碰网在线观看| 黄色美女视频在线观看| 中文字幕第一区综合| 国产精品一区二区你懂得| 中文字幕无码乱码人妻日韩精品| 在线国产日韩| 日韩一级黄色av| 国产免费无遮挡吸奶头视频| 亚洲日本va| 欧美日本高清视频在线观看| 国产99久久九九精品无码| 2020国产在线视频| 中文文精品字幕一区二区| 久久99久久99精品蜜柚传媒| 精品人妻av一区二区三区| 免费看欧美女人艹b| 69影院欧美专区视频| 69av.com| 永久亚洲成a人片777777| 一区二区三区视频在线| 国产肉体xxxx裸体784大胆| 一区中文字幕电影| 欧美一区二区三区思思人| 男生操女生视频在线观看| 色8久久影院午夜场| 精品女同一区二区三区在线播放| 伊人网在线免费| 精品美女在线观看视频在线观看| 欧美高清在线一区| 日韩精品一区二区三区外面 | 成人免费精品视频| 3d精品h动漫啪啪一区二区| 中文字字幕在线中文乱码| 日韩成人dvd| 国产精品久久久久久久久久东京 | 美女被久久久| 欧美在线一级视频| 国产精品午夜影院| 男人天堂欧美日韩| 人人爽久久涩噜噜噜网站| 亚洲欧美综合自拍| 噜噜噜躁狠狠躁狠狠精品视频| 78色国产精品| 精品国产xxx| 久久综合中文| 国产精品中文久久久久久久| 91精品国产乱码久久| 麻豆91在线看| 亚洲www永久成人夜色| av网站在线免费看| 懂色av一区二区夜夜嗨| 好吊妞www.84com只有这里才有精品| 亚洲国产精品国自产拍久久| 日韩一区二区高清| 久久精品夜夜夜夜夜久久| 强制高潮抽搐sm调教高h| 久久国产中文字幕| 日韩视频中文字幕| 人妻久久一区二区| 136国产福利精品导航网址| 992tv成人免费视频| 国产黄色免费观看| 麻豆精品视频在线观看视频| 2019国产精品视频| 亚洲日本香蕉视频| 亚洲国产精华液网站w| 色爽爽爽爽爽爽爽爽| 国产网红女主播精品视频| 午夜a成v人精品| 久久精品网站视频| 麻豆国产一区| 亚洲女人天堂成人av在线| 人妻无码一区二区三区免费| 最新国产精品| 欧洲s码亚洲m码精品一区| 中文字幕日韩第一页| 国产成人午夜视频| 日本一区二区在线视频观看| 精品国产丝袜高跟鞋| 五月激情丁香一区二区三区| 在线免费观看视频黄| 日本免费一区二区三区视频| 亚洲欧美中文日韩在线| 久久中文免费视频| 久久精品麻豆| 国产精品美女黄网| 日本视频在线| 欧美性xxxx18| 在线观看网站黄| 精品高清在线| 性欧美长视频免费观看不卡| 亚洲无码精品在线观看| 不卡一区在线观看| 亚洲一二区在线| 中文字幕在线高清| 日韩一级片在线观看| 精品亚洲aⅴ无码一区二区三区| 午夜欧美精品| 国产美女扒开尿口久久久| 日韩中文字幕观看| 亚洲美女屁股眼交3| 欧美成人免费高清视频| 白嫩白嫩国产精品| 久久精品人人做人人爽| 最新中文字幕第一页| 97se亚洲国产综合自在线| 在线观看污视频| 成人在线视频观看| 亚洲人成在线电影| 日韩 欧美 综合| 欧美不卡在线观看| 日欧美一区二区| 国产精品欧美日韩| 人人妻人人澡人人爽久久av| 亚洲婷婷在线视频| 国产一区二区在线免费播放| 一区二区三区韩国免费中文网站| 久久久噜噜噜久久| 亚洲国产成人一区二区| 亚洲精品第1页| caoporm在线视频| 成人影视亚洲图片在线| 国产精品jizz在线观看麻豆| 日本福利午夜视频在线| 性做久久久久久久免费看| 香蕉视频在线观看黄| 欧美激情一区| 91pron在线| 2024最新电影免费在线观看| 欧美一二三区在线| 亚洲 欧美 变态 另类 综合| 国产综合色视频| 99久re热视频精品98| 精品一区二区三区中文字幕| 久久久999精品免费| 国产裸体无遮挡| 亚洲男同1069视频| 久久久久无码精品| 亚洲手机视频| 精品不卡在线| 成人性教育av免费网址| 国产一区二区精品丝袜| 懂色av蜜臀av粉嫩av喷吹| 国产女人aaa级久久久级| 污污网站免费观看| 亚洲欧美在线专区| 99r国产精品视频| 后进极品白嫩翘臀在线播放| 亚洲丁香久久久| 99久热在线精品996热是什么| 久久综合五月天婷婷伊人| 日韩免费高清在线| 999久久久国产精品| 91精品在线观看视频| 日本在线视频网址| 亚洲精品久久久久久久久久久久| 亚洲天堂五月天| 中文字幕在线免费不卡| 精品久久久久久无码人妻| 99成人精品| 亚洲成人第一| 亚洲成人五区| 51午夜精品视频| 免费看a在线观看| 欧美成人r级一区二区三区| 日本午夜视频在线观看| 国产精品美日韩| 9191在线视频| 久热综合在线亚洲精品| 亚洲精品偷拍视频| 日韩美女国产精品| 国产日韩精品综合网站| 国产精品yjizz视频网| 中文字幕免费国产精品| 亚洲AV无码精品色毛片浪潮| 欧美日韩中文字幕综合视频| 2014亚洲天堂| www.日韩在线| 超碰在线资源站| 久久激情一区| 亚洲啊啊啊啊啊| 精品一级毛片| 国产精品嫩草在线观看| 国产精品久久久久久吹潮| 久久人人爽人人爽人人片av高清| 黄色在线网站| 亚洲精品在线三区| 国产女人高潮毛片| 色94色欧美sute亚洲13| 黄色一级片在线| 国产精品视频一二三区| 亚洲无人区码一码二码三码| 久久国产精品99久久人人澡| 欧美二区在线视频| 午夜久久美女| 中文字幕成人一区| 国产一区二区亚洲| 精品一区2区三区| 视频精品一区二区三区| 国产精自产拍久久久久久| 国产高清中文字幕在线| 久久在线精品视频| av片在线免费观看| 亚洲免费福利视频| 天堂中文在线观看视频| 欧美一区二区三区四区久久| 中文字幕日韩经典| 91久久精品网| 亚洲av中文无码乱人伦在线视色| 亚洲午夜电影网| 麻豆疯狂做受xxxx高潮视频| 国产精品久久久久久久久搜平片 | 国产在线播放一区二区三区| 国产av无码专区亚洲精品| 亚洲激情精品| 无码 制服 丝袜 国产 另类| 自由日本语亚洲人高潮| 中文字幕一区二区三区乱码| 青草国产精品| 亚洲mv在线看| 精品成人影院| 日韩国产一区久久| 欧美人妖在线| 日本一区二区三区四区高清视频 | 日韩精品在线视频免费观看| 五月天久久久| 青青草免费在线视频观看| 亚洲一区色图| 妺妺窝人体色www看人体| 欧美精品一区二区三区久久久竹菊| 中文字幕一区综合| 亚洲老妇激情| 久草免费福利在线| 99精品视频免费全部在线| 欧美 日韩 国产一区| 久久精品中文| www.亚洲高清| 狠狠色丁香婷婷综合| 91aaa精品| 国产成人精品1024| 丰满岳乱妇一区二区| 99re热这里只有精品视频| 日本aaa视频| 日本一区二区三区视频视频| 一本一本久久a久久| 亚洲男帅同性gay1069| 国产一级片免费| 欧美性猛交xxxx久久久| 国产精品成人无码| 在线播放亚洲一区| www.麻豆av| 亚洲激情视频网站| 国产乱子伦三级在线播放| 日韩视频欧美视频| 日韩伦理av| 国产97在线|亚洲| 亚洲国产天堂| 国产另类自拍| 欧美偷拍综合| 大西瓜av在线| 蜜臀av一区二区在线免费观看 | 中文字幕av第一页| 91麻豆精品国产91久久久使用方法 | 免费精品视频| 999这里有精品| 成人一级片网址| 久久久久久九九九九九| 国产精品国产a级| 国产成人精品片| 欧美日韩国产高清一区二区三区| 亚洲第一色网站| 亚洲色图av在线| 影音先锋中文在线视频| 欧美在线亚洲在线| 国产情侣一区在线| 麻豆成人在线播放| 亚洲精品91| 日日碰狠狠躁久久躁婷婷| 国产v日产∨综合v精品视频| brazzers精品成人一区| 亚洲黄色片在线观看| 日批视频免费在线观看| 欧美成人性福生活免费看| 国产人成在线视频| 欧美精品video| 亚洲资源在线| 欧美国产一区二区在线| 国模吧视频一区| 九九热99视频| 国产亚洲精品免费| 国产在线拍揄自揄拍| 欧美日本韩国一区二区三区视频| 亚洲欧美自偷自拍| 欧美日本黄视频| 成人国产精品久久| 日韩激情视频| 久久九九电影| 亚洲国产第一区| 亚洲乱码中文字幕综合| а中文在线天堂| 精品视频—区二区三区免费| 日日夜夜天天综合入口| 成人国产精品日本在线| 国内成人自拍| 日本三级免费网站| 99久久免费精品高清特色大片| 91视频免费在线看| 欧美军同video69gay| 69久久精品| 国产精品激情自拍| 九九热线有精品视频99| 国产特级黄色大片| 成人激情免费网站| 伊人365影院| 精品少妇一区二区三区日产乱码 | 四虎影视在线播放| 亚州成人av在线| 成人台湾亚洲精品一区二区| 在线观看av的网址| 国产美女在线观看一区| 手机在线免费看片| 欧美一区二区视频观看视频| 黄色成人影院| 2019国产精品视频| 国产综合精品| 国产 xxxx| 欧美日韩亚洲天堂| 久久久资源网| 国产精品黄色影片导航在线观看| 精品国精品国产自在久国产应用| 午夜视频在线瓜伦| 中文字幕+乱码+中文字幕一区| 国产精品无码一区| 日韩亚洲第一页| 欧美激情三级| 9色porny| 久久影音资源网| 亚洲大尺度在线观看| 中文字幕在线亚洲| 国产精品2区| 国内精品视频一区二区三区| 久久综合色综合88| 国产精品51麻豆cm传媒 | 麻豆一区在线观看| 91精品国产综合久久久久| 超碰公开在线| 精品不卡在线| 美美哒免费高清在线观看视频一区二区| 久久久久亚洲AV成人无在| 欧美福利视频一区| 三级资源在线| 日本公妇乱淫免费视频一区三区| 日韩av中文字幕一区二区| 性生交大片免费全黄| 欧美大片日本大片免费观看| 午夜不卡影院| 亚洲图片欧洲图片日韩av| 国产福利91精品一区二区三区| 日韩三级av在线| 在线电影中文日韩| 日韩欧美久久| 六月丁香婷婷激情| 国产精品对白交换视频| 丰满肉嫩西川结衣av| 欧美专区日韩视频| 亚洲天天综合| 亚洲午夜久久久久久久久红桃| 91 com成人网| 色一区二区三区| 自拍另类欧美| 91视频一区二区| 国产又色又爽又黄又免费| 午夜精品免费视频| 欧美成人milf| aa一级黄色片| 91精品国产综合久久久久久久久久| 波多野结衣视频一区二区| 午夜精品区一区二区三| 99在线精品观看| 国产成人三级在线播放| 国产成人免费av电影| 国内精品久久久久久久影视蜜臀 |