Sa-Token

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


提示:以下是本篇文章正文内容,下面案例可供参考

一、Sa-Token介绍

Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权 等一系列权限相关问题

通俗的来讲,就是解决了我们哪些接口是需要先登录后调用的,哪些接口是添加了白名单,不需要登录就可以调用的,Sa-Token提供了简便的API及配置,能让开发更快、更高效。

二、Sa-Token使用场景

1.鉴权(登录拦截)

public class SaTokenAuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws IOException {
        if (StpUtil.isLogin()) {
            return true;
        }

        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.getWriter().append(JSONUtil.toJsonStr(ResultVO.fail(ResultVOEnum.NO_AUTH)));
        return false;
    }
}

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册Sa-Token的认证截器
        registry.addInterceptor(new SaTokenAuthInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/auth/**", "/actuator/**", "/callback/**", "/health");
    }
}

上边的代码,添加了一个登录拦截器,在所有接口执行之前,先执行拦截器内的代码逻辑。

上边这个例子里,添加的逻辑是判断是否已登录,如果已登录返回true,未登录返回false,并将结果回写。 然后将拦截器注册,同时可以添加拦截路径以及白名单。

public boolean isLogin() {
    return this.getLoginIdDefaultNull() != null;
}

public Object getLoginIdDefaultNull() {
    if (this.isSwitch()) {
        return this.getSwitchLoginId();
    } else {
        String tokenValue = this.getTokenValue();
        if (tokenValue == null) {
            return null;
        } else {
            Object loginId = this.getLoginIdNotHandle(tokenValue);
            if (!this.isValidLoginId(loginId)) {
                return null;
            } else {
                return this.getTokenActivityTimeoutByToken(tokenValue) == -2L ? null : loginId;
            }
        }
    }
}

public String getTokenValue() {
    String tokenValue = this.getTokenValueNotCut();
    String tokenPrefix = this.getConfig().getTokenPrefix();
    if (!SaFoxUtil.isEmpty(tokenPrefix)) {
        if (!SaFoxUtil.isEmpty(tokenValue) && tokenValue.startsWith(tokenPrefix + " ")) {
            tokenValue = tokenValue.substring(tokenPrefix.length() + " ".length());
        } else {
            tokenValue = null;
        }
    }

    return tokenValue;
}

public String getTokenValueNotCut() {
    SaStorage storage = SaHolder.getStorage();
    SaRequest request = SaHolder.getRequest();
    SaTokenConfig config = this.getConfig();
    String keyTokenName = this.getTokenName();
    String tokenValue = null;
    if (storage.get(this.splicingKeyJustCreatedSave()) != null) {
        tokenValue = String.valueOf(storage.get(this.splicingKeyJustCreatedSave()));
    }

    if (tokenValue == null && config.getIsReadBody()) {
        tokenValue = request.getParam(keyTokenName);
    }

    if (tokenValue == null && config.getIsReadHead()) {
        tokenValue = request.getHeader(keyTokenName);
    }

    if (tokenValue == null && config.getIsReadCookie()) {
        tokenValue = request.getCookieValue(keyTokenName);
    }

    return tokenValue;
}

这段代码就是Sa-Token中的判断是否登录的源码,可以看出判断登录是从请求头中获取的登录信息,下边是一个实际案例截图

 

2.登录

说完登录拦截之后,再补充一下登录

public static void login(Object id) {
    stpLogic.login(id);
}

public void login(Object id) {
    this.login(id, new SaLoginModel());
}

public void login(Object id, SaLoginModel loginModel) {
    String token = this.createLoginSession(id, loginModel);
    this.setTokenValue(token, loginModel.getCookieTimeout());
}

public void setTokenValue(String tokenValue, int cookieTimeout) {
    if (!SaFoxUtil.isEmpty(tokenValue)) {
        this.setTokenValueToStorage(tokenValue);
        if (this.getConfig().getIsReadCookie()) {
            this.setTokenValueToCookie(tokenValue, cookieTimeout);
        }

    }
}

public void setTokenValueToStorage(String tokenValue) {
    SaStorage storage = SaHolder.getStorage();
    String tokenPrefix = this.getConfig().getTokenPrefix();
    if (!SaFoxUtil.isEmpty(tokenPrefix)) {
        storage.set(this.splicingKeyJustCreatedSave(), tokenPrefix + " " + tokenValue);
    } else {
        storage.set(this.splicingKeyJustCreatedSave(), tokenValue);
    }

    storage.set("JUST_CREATED_NOT_PREFIX_", tokenValue);
}

public void set(String key, Object value) {
    this.request.setAttribute(key, value);
}

public String createLoginSession(Object id, SaLoginModel loginModel) {
    SaTokenException.throwByNull(id, "账号id不能为空");
    if (this.isDisable(id)) {
        throw new DisableLoginException(this.loginType, id, this.getDisableTime(id));
    } else {
        SaTokenConfig config = this.getConfig();
        loginModel.build(config);
        String tokenValue = null;
        if (config.getIsConcurrent()) {
            if (this.getConfigOfIsShare() && (loginModel.getExtraData() == null || loginModel.getExtraData().size() == 0)) {
                tokenValue = this.getTokenValueByLoginId(id, loginModel.getDeviceOrDefault());
            }
        } else {
            this.replaced(id, loginModel.getDevice());
        }

        if (tokenValue == null) {
            if (SaFoxUtil.isEmpty(loginModel.getToken())) {
                tokenValue = this.createTokenValue(id, loginModel.getDeviceOrDefault(), loginModel.getTimeout(), loginModel.getExtraData());
            } else {
                tokenValue = loginModel.getToken();
            }
        }

        SaSession session = this.getSessionByLoginId(id, true);
        session.updateMinTimeout(loginModel.getTimeout());
        session.addTokenSign(tokenValue, loginModel.getDeviceOrDefault());
        this.saveTokenToIdMapping(tokenValue, id, loginModel.getTimeout());
        this.setLastActivityToNow(tokenValue);
        SaManager.getSaTokenListener().doLogin(this.loginType, id, tokenValue, loginModel);
        if (config.getMaxLoginCount() != -1) {
            this.logoutByMaxLoginCount(id, session, (String)null, config.getMaxLoginCount());
        }

        return tokenValue;
    }
}

public SaSession getSessionBySessionId(String sessionId, boolean isCreate) {
    SaSession session = this.getSaTokenDao().getSession(sessionId);
    if (session == null && isCreate) {
        session = (SaSession)SaStrategy.me.createSession.apply(sessionId);
        this.getSaTokenDao().setSession(session, this.getConfig().getTimeout());
    }

    return session;
}

default SaSession getSession(String sessionId) {
    return (SaSession)this.getObject(sessionId);
}

// 从redis中读取
public Object getObject(String key) {
    return this.objectRedisTemplate.opsForValue().get(key);
}

// 从内存中读取
public Object getObject(String key) {
    this.clearKeyByTimeout(key);
    return this.dataMap.get(key);
}

public class SaTokenConfig implements Serializable {
    private static final long serialVersionUID = -6541180061782004705L;
    // 重要
    private String tokenName = "satoken";
    // 重要
    private long timeout = 2592000L;
    private long activityTimeout = -1L;
    private Boolean isConcurrent = true;
    private Boolean isShare = true;
    private int maxLoginCount = 12;
    private Boolean isReadBody = true;
    private Boolean isReadHead = true;
    private Boolean isReadCookie = true;
    private String tokenStyle = "uuid";
    private int dataRefreshPeriod = 30;
    private Boolean tokenSessionCheckLogin = true;
    private Boolean autoRenew = true;
    private String tokenPrefix;
    private Boolean isPrint = true;
    private Boolean isLog = false;
    private String jwtSecretKey;
    private long idTokenTimeout = 86400L;
    private String basic = "";
    private String currDomain;
    private Boolean checkIdToken = false;
    public SaCookieConfig cookie = new SaCookieConfig();

    public SaTokenConfig() {
    }

    ......
}

 以上为登录逻辑重要的源码,登录的调用特别简单,StpUtil.login(customer.getGuid());   customer.getGuid()为用户的唯一标识,一个用户对应一个

由此可以看出,Sa-Token已经帮研发人员将逻辑都封装好了,接入还是比较简单。

3.单点登录

先登出

StpUtil.logout(Object loginId)

然后更新loginId,新生成一个loginId

StpUtil.login(loginId)

此时新的登录生效,原登录失效,原登录状态下继续操作,会被登录拦截,提示未登录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值