一、需求分析

在前文,咱们具体的叙述了在 Sa-Token 怎么运用注解进行权限认证,注解鉴权虽然便利,却并不适合一切鉴权场景。

假设有如下需求:项目中一切接口均需求登录认证校验,只有 “登录接口” 自身对外敞开。

假如咱们对项目一切接口都加上 @SaCheckLogin 注解,会显得非常冗余且没有必要,在这个需求中咱们真实需求的是一种依据路由阻拦的鉴权模式,那么在 Sa-Token 怎么完成路由阻拦鉴权呢?

Sa-Token 是一个轻量级 java 权限认证结构,主要处理登录认证、权限认证、单点登录、OAuth2、微服务网关鉴权 等一系列权限相关问题。
Gitee 开源地址:gitee.com/dromara/sa-…

二、注册 Sa-Token 路由阻拦器

首先在项目中引进 Sa-Token 依赖:

<!-- Sa-Token 权限认证 -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-spring-boot-starter</artifactId>
    <version>1.34.0</version>
</dependency>

注:假如你运用的是 SpringBoot 3.x,只需求将 sa-token-spring-boot-starter 修改为 sa-token-spring-boot3-starter 即可。

新建配置类SaTokenConfigure.java

@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
	// 注册阻拦器
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		// 注册 Sa-Token 阻拦器,校验规则为 StpUtil.checkLogin() 登录校验。
		registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
				.addPathPatterns("/**")
				.excludePathPatterns("/user/doLogin"); 
	}
}

以上代码,咱们注册了一个依据 StpUtil.checkLogin() 的登录校验阻拦器,并且扫除了/user/doLogin接口用来敞开登录(除了/user/doLogin以外的一切接口都需求登录才干拜访)。

SaInterceptor 是新版本供给的阻拦器,点此 查看旧版本代码搬迁示例。

三、校验函数详解

自定义认证规则:new SaInterceptor(handle -> StpUtil.checkLogin()) 是最简单的写法,代表只进行登录校验功用。

咱们能够往结构函数塞一个完整的 lambda 表达式,来定义具体的校验规则,例如:

@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		// 注册 Sa-Token 阻拦器,定义具体认证规则 
		registry.addInterceptor(new SaInterceptor(handler -> {
			// 指定一条 match 规则
			SaRouter
				.match("/**")	// 阻拦的 path 列表,能够写多个 */
				.notMatch("/user/doLogin")		// 扫除去的 path 列表,能够写多个 
				.check(r -> StpUtil.checkLogin());		// 要履行的校验动作,能够写完整的 lambda 表达式
			// 依据路由区分模块,不同模块不同鉴权 
			SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
			SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
			SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
			SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));
			SaRouter.match("/notice/**", r -> StpUtil.checkPermission("notice"));
			SaRouter.match("/comment/**", r -> StpUtil.checkPermission("comment"));
		})).addPathPatterns("/**");
	}
}

SaRouter.match() 匹配函数有两个参数:

  • 参数一:要匹配的path路由。
  • 参数二:要履行的校验函数。

在校验函数内不只能够运用 StpUtil.checkPermission("xxx") 进行权限校验,你还能够写恣意代码,例如:

@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
	// 注册 Sa-Token 的阻拦器
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		// 注册路由阻拦器,自定义认证规则 
		registry.addInterceptor(new SaInterceptor(handler -> {
			// 登录校验 -- 阻拦一切路由,并扫除/user/doLogin 用于敞开登录 
			SaRouter.match("/**", "/user/doLogin", r -> StpUtil.checkLogin());
			// 人物校验 -- 阻拦以 admin 最初的路由,有必要具有 admin 人物或许 super-admin 人物才能够通过认证 
			SaRouter.match("/admin/**", r -> StpUtil.checkRoleOr("admin", "super-admin"));
			// 权限校验 -- 不同模块校验不同权限 
			SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
			SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
			SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
			SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));
			SaRouter.match("/notice/**", r -> StpUtil.checkPermission("notice"));
			SaRouter.match("/comment/**", r -> StpUtil.checkPermission("comment"));
			// 乃至你能够随意的写一个打印语句
			SaRouter.match("/**", r -> System.out.println("----啦啦啦----"));
            // 连缀写法
            SaRouter.match("/**").check(r -> System.out.println("----啦啦啦----"));
		})).addPathPatterns("/**");
	}
}

四、匹配特征详解

除了上述示例的 path 路由匹配,还能够依据许多其它特征进行匹配,以下是一切可匹配的特征:

// 基础写法样例:匹配一个path,履行一个校验函数 
SaRouter.match("/user/**").check(r -> StpUtil.checkLogin());
// 依据 path 路由匹配   ——— 支撑写多个path,支撑写 restful 风格路由 
// 功用阐明: 运用 /user , /goods 或许 /art/get 最初的恣意路由都将进入 check 办法
SaRouter.match("/user/**", "/goods/**", "/art/get/{id}").check( /* 要履行的校验函数 */ );
// 依据 path 路由扫除匹配 
// 功用阐明: 运用 .html , .css 或许 .js 结尾的恣意路由都将跳过, 不会进入 check 办法
SaRouter.match("/**").notMatch("*.html", "*.css", "*.js").check( /* 要履行的校验函数 */ );
// 依据恳求类型匹配 
SaRouter.match(SaHttpMethod.GET).check( /* 要履行的校验函数 */ );
// 依据一个 boolean 条件进行匹配 
SaRouter.match( StpUtil.isLogin() ).check( /* 要履行的校验函数 */ );
// 依据一个回来 boolean 成果的lambda表达式匹配 
SaRouter.match( r -> StpUtil.isLogin() ).check( /* 要履行的校验函数 */ );
// 多个条件一起运用 
// 功用阐明: 有必要是 Get 恳求 并且 恳求路径以 `/user/` 最初 
SaRouter.match(SaHttpMethod.GET).match("/user/**").check( /* 要履行的校验函数 */ );
// 能够无限连缀下去 
// 功用阐明: 同时满意 Get 办法恳求, 且路由以 /admin 最初, 路由中间带有 /send/ 字符串, 路由结尾不能是 .js 和 .css
SaRouter
	.match(SaHttpMethod.GET)
	.match("/admin/**")
	.match("/**/send/**") 
	.notMatch("/**/*.js")
	.notMatch("/**/*.css")
	// ....
	.check( /* 只有上述一切条件都匹配成功,才会履行最终的check校验函数 */ );

五、提早退出匹配链

运用 SaRouter.stop() 能够提早退出匹配链,例:

registry.addInterceptor(new SaInterceptor(handler -> {
	SaRouter.match("/**").check(r -> System.out.println("进入1"));
	SaRouter.match("/**").check(r -> System.out.println("进入2")).stop();
	SaRouter.match("/**").check(r -> System.out.println("进入3"));
	SaRouter.match("/**").check(r -> System.out.println("进入4"));
	SaRouter.match("/**").check(r -> System.out.println("进入5"));
})).addPathPatterns("/**");

如上示例,代码运行至第2条匹配链时,会在stop函数处提早退出整个匹配函数,从而疏忽掉剩下的一切match匹配

除了stop()函数,SaRouter还供给了 back() 函数,用于:中止匹配,结束履行,直接向前端回来成果

// 履行back函数后将中止匹配,也不会进入Controller,而是直接将 back参数 作为回来值输出到前端
SaRouter.match("/user/back").back("要回来到前端的内容");

stop() 与 back() 函数的差异在于:

  • SaRouter.stop() 会中止匹配,进入Controller。
  • SaRouter.back() 会中止匹配,直接回来成果到前端。

六、运用free翻开一个独立的效果域

// 进入 free 独立效果域 
SaRouter.match("/**").free(r -> {
	SaRouter.match("/a/**").check(/* --- */);
	SaRouter.match("/b/**").check(/* --- */).stop();
	SaRouter.match("/c/**").check(/* --- */);
});
// 履行 stop() 函数跳出 free 后持续履行下面的 match 匹配 
SaRouter.match("/**").check(/* --- */);

free() 的效果是:翻开一个独立的效果域,使内部的 stop() 不再一次性跳出整个 Auth 函数,而是仅仅跳出当时 free 效果域。

七、运用注解疏忽掉路由阻拦校验

咱们能够运用 @SaIgnore 注解,疏忽掉路由阻拦认证:

1、先配置好了阻拦规则:

@Override
public void addInterceptors(InterceptorRegistry registry) {
	registry.addInterceptor(new SaInterceptor(handler -> {
		// 依据路由区分模块,不同模块不同鉴权 
		SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
		SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
		SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
		// ... 
	})).addPathPatterns("/**");
}

2、然后在 Controller 里又添加了疏忽校验的注解

@SaIgnore
@RequestMapping("/user/getList")
public SaResult getList() {
	System.out.println("------------ 拜访进来办法"); 
	return SaResult.ok(); 
}

恳求将会跳过阻拦器的校验,直接进入 Controller 的办法中。

留意点:此注解的疏忽效果只针对 SaInterceptor阻拦器 和 AOP注解鉴权 收效,对自定义阻拦器与过滤器不收效。

八、封闭注解校验

SaInterceptor 只要注册到项目中,默许就会翻开注解校验,假如要封闭此才能,需求:

@Override
public void addInterceptors(InterceptorRegistry registry) {
	registry.addInterceptor(
		new SaInterceptor(handle -> {
			SaRouter.match("/**").check(r -> StpUtil.checkLogin());
		}).isAnnotation(false)  // 指定封闭掉注解鉴权才能,这样结构就只会做路由阻拦校验了 
	).addPathPatterns("/**");
}

参考资料

  • Sa-Token 文档:sa-token.cc
  • Gitee 库房地址:gitee.com/dromara/sa-…
  • GitHub 库房地址:github.com/dromara/sa-…