一、Satoken原理
Satoken是基於Cookie與Session的一種輕量級權限認證框架,它通過將登錄成功後的用戶信息存儲在session中,並基於此生成一個token,再將token存儲在cookie中返回給客戶端,從而實現身份認證和權限校驗的功能。
Satoken的核心代碼如下:
public class SaTokenManager {
/**
* 創建指定user_id的token
* @param loginId 賬號id
* @return token
*/
public String createToken(Object loginId) {
// 生成 sessionId
String sessionId = SessionIdGenerator.generateId();
// 創建 session
SaSession session = SaManager.getSaSession(sessionId, true);
// 標記用戶已登錄
session.setAttribute("loginId", loginId);
// 創建 Session-Token
String tokenValue = TokenValueGenerator.generateValue();
SaToken token = SaManager.createToken(tokenValue);
// 關聯 session
SaManager.bindToken(token, session);
// 將 session id 寫到 cookie
SaManager.getResponse().addCookie(SaConstant.TOKEN_NAME, tokenValue);
return tokenValue;
}
/**
* 刪除指定tokenId對應的session
* @param tokenValue tokenId
*/
public void removeToken(String tokenValue) {
SaManager.logoutByTokenValue(tokenValue);
}
/**
* 根據Token-Value 獲取對應userid
* @param tokenValue Token值
* @return userid,如果未登錄則返回null
*/
public Object getUserIdByToken(String tokenValue) {
return SaManager.getObjectValue(tokenValue, "loginId");
}
/**
* 根據Token-Value 獲取對應session
* @param tokenValue Token值
* @return session,如果未登錄則返回null
*/
public SaSession getSessionByToken(String tokenValue) {
return SaManager.getSessionByToken(tokenValue);
}
}
二、Satoken跨域
Satoken可以支持跨域訪問,只需要在服務器端配置一下即可。例如,我們可以使用SpringBoot來進行配置:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
三、Goldengate原理
Satoken集成了類似於Goldengate的功能,可以在登陸成功後將用戶信息同步到其他應用的session中。具體實現可以繼承SaTokenListener,實現onLogout、onLoginSuccess等方法即可。
以下是一個典型的Goldengate應用的代碼示例:
public class GoldenGateSaTokenListener implements SaTokenListener {
/**
* 登錄成功後同步用戶信息到其他應用
*/
@Override
public void onLoginSuccess(String loginId, ServletRequest request, SaToken token) {
// 獲取當前應用的所有Url
List<String> urlList = getApplicationUrls();
// 將用戶信息同步到其他應用
for (String url : urlList) {
HttpUtil.post(url + "/goldengate/syncUser", loginId);
}
}
/**
* 用戶退出時通知其他應用刪除該用戶的session
*/
@Override
public void onLogout(String loginId, ServletRequest request, SaToken token) {
// 獲取當前應用的所有Url
List<String> urlList = getApplicationUrls();
// 將退出消息通知其他應用
for (String url : urlList) {
HttpUtil.post(url + "/goldengate/removeUser", loginId);
}
}
/**
* 獲取當前應用所有Url
*/
private List<String> getApplicationUrls() {
// 省略獲取Url列表的代碼
}
}
四、Satoken真的好用嗎
Satoken具有輕量、易用、高性能、安全等優勢,其使用非常簡單,十分適合做權限認證。此外,Satoken還具備 Goldengate等高級功能,可輕鬆支持微服務、分佈式等場景。因此,Satoken值得我們去嘗試使用。
五、Satoken權限認證
Satoken具有基於角色、權限、URL等粒度的權限認證功能,使用示例如下:
public class MyController {
/**
* 需要admin權限
* @return
*/
@SaCheckRole("admin")
@GetMapping("/manager")
public String manager() {
return "manager";
}
/**
* 需要user或admin權限
* @return
*/
@SaCheckPermission("user:read")
@PostMapping("/user/list")
public String userList() {
return "userList";
}
}
六、Satoken官網
Satoken的官網為:https://sa-token.dev33.cn/
七、Satoken怎麼樣
Satoken在功能上與傳統的Shiro、Spring Security等認證框架相比並沒有多大的優勢,但它的性能要更加高效,並且使用起來非常方便,適合小型項目的開發。
八、Satoken微服務
Satoken完全支持微服務的使用,例如在Spring Cloud微服務架構中,可以通過配置SaTokenServerConfigurer來實現跨微服務的數據同步。代碼示例如下:
@Configuration
public class SaTokenServerConfig implements SaTokenServerConfigurer {
@Autowired
private DiscoveryClient discoveryClient;
@Override
public boolean refreshTokenById(String tokenValue, String tokenKey, String tokenId) {
// 根據tokenId在所有微服務中查找該token對應的session,然後重新生成token
List<ServiceInstance> instances = discoveryClient.getInstances(SERVER_APP_NAME);
for (ServiceInstance instance : instances) {
String url = instance.getUri() + "/api/token/refresh?id=" + tokenId;
try {
restTemplate.put(url, null);
return true;
} catch (RestClientException e) {
e.printStackTrace();
}
}
return false;
}
}
九、Satoken Gateway
Satoken也支持在Spring Cloud Gateway中使用,只需要在gateway中添加一個TokenFilter即可,該filter可以實現用戶身份認證、權限校驗等功能。具體實現可以參考以下代碼:
public class TokenFilter implements GatewayFilter, Ordered {
@Autowired
private UserService userService;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 認證用戶身份
String tokenValue = getAuthorizationToken(exchange.getRequest());
SaToken token = SaManager.checkToken(tokenValue);
if (!token.isLogin()) {
return ResponseUtil.unauthorized(exchange, "未登錄或已過期");
}
// 校驗用戶權限
String uri = exchange.getRequest().getURI().getPath();
boolean hasPermission = userService.checkPermission(token.getObjectId(), uri);
if (!hasPermission) {
return ResponseUtil.forbidden(exchange);
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -200;
}
}
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/201160.html