您的位置:

Satoken原理详解

一、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;
    }
}