springboot集成Sa-Token
版本
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.28.0</version>
</dependency>
</dependencies>
登录/登出
- StpUtil.login方法会创建一个token,并将token放到cookie中,保存到前端,前端再次访问时会携带此token。StpUtil.isLogin()会根据cookie中的token信息判断用户是否登录。
- 通过cookie实现的,不同浏览器,不同终端不能共享登录的token,需要重新登录。
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public SaResult login(@RequestParam String mobile, @RequestParam String password){
StpUtil.login(10000);
return SaResult.ok(StpUtil.getTokenValue());
}
@RequestMapping("isLogin")
public String isLogin() {
return "当前会话是否登录:" + StpUtil.isLogin();
}
@RequestMapping("logout")
public String logout() {
StpUtil.logout(10000);
return "当前会话是否登录:" + StpUtil.isLogin();
}
@RequestMapping("getPermissionList")
public SaResult getPermissionList() {
List<String> permissionList = StpUtil.getPermissionList();
List<String> roleList = StpUtil.getRoleList();
permissionList.addAll(roleList);
return SaResult.data(permissionList);
}
}
权限验证
- 实现StpInterface接口,重写getPermissionList和getRoleList方法,实际业务中可以将用户的权限和角色维护到数据库中。
- 程序启动时并不会调用此类的方法,在调用StpUtil.getPermissionList();或者StpUtil.getRoleList();会调用。
@Component // 保证此类被 SpringBoot 扫描,完成 Sa-Token 的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface {
/**
* 返回一个账号所拥有的权限码集合
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
// 本 list 仅做模拟,实际项目中要根据具体业务逻辑来查询权限
//根据传入的loginId从数据库(或缓存)中查询具体的权限
List<String> list = new ArrayList<String>();
list.add("101");
list.add("user.add");
list.add("user.update");
list.add("user.get");
// list.add("user.delete");
list.add("art.*");
return list;
}
/**
* 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
*/
@Override
public List<String> getRoleList(Object loginId, String loginType) {
// 本 list 仅做模拟,实际项目中要根据具体业务逻辑来查询角色
//根据传入的loginId从数据库(或缓存)中查询具体的权限
List<String> list = new ArrayList<String>();
list.add("admin");
list.add("super-admin");
return list;
}
}
获取权限源码分析
#获取用户所有的菜单按钮权限
StpUtil.getPermissionList();
#那么是如何调用我们自定义的StpInterfaceImpl类获取权限的呢?
#StpUtil.getPermissionList()最终调用此方法获取权限
public List<String> getPermissionList(Object loginId) {
return SaManager.getStpInterface().getPermissionList(loginId, loginType);
}
#SaManager.getStpInterface() 如果stpInterface为空则返回默认的StpInterfaceDefaultImpl对象
#如果不为空则返回stpInterface
public static StpInterface getStpInterface() {
if (stpInterface == null) {
synchronized (SaManager.class) {
if (stpInterface == null) {
SaManager.stpInterface = new StpInterfaceDefaultImpl();
}
}
}
return stpInterface;
}
#那么stpInterface是如何初始化为我们自定义的StpInterfaceImpl的呢?
#项目启动时sa-token-spring-boot-autoconfigure包下cn.dev33.satoken.spring.SaBeanInject会注入我们自定义的类
#@Component
#public class StpInterfaceImpl implements StpInterface
/**
* 注入权限认证Bean
*
* @param stpInterface StpInterface对象
*/
@Autowired(required = false)
public void setStpInterface(StpInterface stpInterface) {
SaManager.setStpInterface(stpInterface);
}
#项目启动后SaManager的属性stpInterface已经被初始化为我们的StpInterfaceImpl实例,项目中调用后StpUtil.getPermissionList();后则会调用我们重新的getPermissionList()方法。
下线
强制注销等价于对方主动调用了注销方法,再次访问会提示:Token无效。
踢人下线不会清除Token信息,而是将其打上特定标记,再次访问会提示:Token已被踢下线。
StpUtil.logout(10001); // 强制指定账号注销下线
StpUtil.logout(10001, "PC"); // 强制指定账号指定端注销下线
StpUtil.logoutByTokenValue("token"); // 强制指定 Token 注销下线
StpUtil.kickout(10001); // 将指定账号踢下线
StpUtil.kickout(10001, "PC"); // 将指定账号指定端踢下线
StpUtil.kickoutByTokenValue("token"); // 将指定 Token 踢下线
SSO单点登录
前端同域,后端同redis
- 客户端前端同域,则cookie可以存在相同的域名或顶级域名下,一个客户端登录成功后,将token信息保存到域名下的cookie中
- 其他不同客户端访问时,因为域名或者顶级域名相同,也能取到域名下的cookie中的token信息并传到后端
前端不同域,后端同redis
-
由于前端客户端不同域,那么不同客户端登录后,保存cookie中的token信息其他客户端并不能取到。因此需要一个能够传递token媒介。
-
认证信息的service端只有一个域名,因此把认证信息的service端域名下的cookie作为传输的媒介。
-
当客户端1登录时,客户端1未登录,则重定向到service端进行登录(携带着客户端自己的地址,等认证通过后跳转回来),sevice端弹出登录界面,输入账号、密码登录完成后,将token信息保存到service域名的cookie下。
-
service已登录,创建ticket(创建一个key与账号的缓存)信息并携带ticket信息重定向到到客户端1,客户端1根据ticket中的账号信息进行登录,并重定像到最初的地址(保存token信息到客户端1域名下的cookie)。
-
客户端2进行登录,客户端2未登录,则重定向到service端进行登录,此时重定像到service端的时候,能够携带sevice端域名下的cookie中的token信息进行验证,验证已登录,则创建ticket(信息并携带ticket信息重定向到到客户端2。
***可能大家会想,用户A认证通过SSO后,通过回调地址将Token返回给原业务系统,原业务系统直接设置登 录状态,这样流程简单,也完成了登录,那为什么还要还要拿Token再次访问SSO进行验证呢?其实这样问题 时很严重的,如果用户在SSO没有登录,而是直接在浏览器中敲入回调的地址,并带上伪造的用户信息,是不 是业务系统也认为登录了呢?这是很可怕的。***