【开源项目】Sa-Token快速登录(使用+源码解析)

什么是Sa-Token

官网:https://sa-token.dev33.cn

Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证权限认证Session会话单点登录OAuth2.0微服务网关鉴权 等一系列权限相关问题。
在这里插入图片描述

快速使用

引入Maven依赖

        <!-- web支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Sa-Token-Quick-Login 插件 -->
        <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-quick-login</artifactId>
            <version>1.33.0</version>
        </dependency>

配置参数

server:
  port: 8080

# Sa-Token-Quick-Login 配置
sa:
  # 登录账号
  name: admin
  # 登录密码
  pwd: 123456
  # 是否自动随机生成账号密码 (此项为true时, name与pwd失效)
  auto: false
  # 是否开启全局认证(关闭后将不再强行拦截)
  auth: true
  # 登录页标题
  title: Charles Index 登录
  # 是否显示底部版权信息
  copr: true
  # 指定拦截路径
  include: /**
  # 指定排除路径
  exclude: /test

编写Controller

@RestController
public class TestController {

    /**
     * 不需要认证
     *
     * @return
     */
    @GetMapping("test")
    public String test() {
        return "test";
    }

    /**
     * 需要认证
     *
     * @return
     */
    @GetMapping("test1")
    public String test1() {
        return "test1";
    }
}

测试

  • 访问 http://localhost:8080/test1
    在这里插入图片描述

由于没有登录,被拦截了,到了登录页面

  • 访问 http://localhost:8080/test,可以获取数据。
    在这里插入图片描述

源码解析

  1. 引入sa-token-quick-login,会加载jar包中的spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.quick.SaQuickInject
  1. SaQuickInject类上面注解是@Import({SaQuickController.class, SaQuickRegister.class}),所以会添加SaQuickControllerSaQuickRegister
  2. SaQuickController主要是提供登录页面和提供登录接口
@Controller
public class SaQuickController {
    public SaQuickController() {
    }

    @GetMapping({"/saLogin"})
    public String saLogin(HttpServletRequest request) {
        request.setAttribute("cfg", SaQuickManager.getConfig());
        return "sa-login.html";
    }

    @PostMapping({"/doLogin"})
    @ResponseBody
    public SaResult doLogin(String name, String pwd) {
        if (!SaFoxUtil.isEmpty(name) && !SaFoxUtil.isEmpty(pwd)) {
            SaQuickConfig config = SaQuickManager.getConfig();
            if (name.equals(config.getName()) && pwd.equals(config.getPwd())) {
                StpUtil.login(config.getName());
                return SaResult.get(200, "ok", StpUtil.getTokenInfo());
            } else {
                return SaResult.get(500, "账号或密码输入错误", (Object)null);
            }
        } else {
            return SaResult.get(500, "请输入账号和密码", (Object)null);
        }
    }
}
  1. SaQuickRegister是核心,提供了SaServletFilterSaServletFilter用来做拦截认证。
@Configuration
public class SaQuickRegister {
    public SaQuickRegister() {
    }

    @Bean
    @ConfigurationProperties(
        prefix = "sa"
    )
    public SaQuickConfig getSaQuickConfig() {
        return new SaQuickConfig();
    }

    @Bean
    @Order(-101)
    public SaServletFilter getSaServletFilter() {
        return (new SaServletFilter()).addInclude(new String[]{"/**"}).addExclude(new String[]{"/favicon.ico", "/saLogin", "/doLogin", "/sa-res/**"}).setAuth((obj) -> {
            SaRouter.match(SaQuickManager.getConfig().getInclude().split(",")).notMatch(SaQuickManager.getConfig().getExclude().split(",")).check((r) -> {
                if (SaQuickManager.getConfig().getAuth() && !StpUtil.isLogin()) {
                    SaHolder.getRequest().forward("/saLogin");
                    SaRouter.back();
                }

            });
        }).setError((e) -> {
            return e.getMessage();
        });
    }
}
  1. 访问http://localhost:8080/test,看下SaServletFilter是如何放行的。
    // cn.dev33.satoken.filter.SaServletFilter#doFilter
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
            SaRouter.match(this.includeList).notMatch(this.excludeList).check((r) -> {
                this.beforeAuth.run((Object)null);
                this.auth.run((Object)null);
            });
        } catch (StopMatchException var6) {
        } catch (Throwable var7) {
            String result = var7 instanceof BackResultException ? var7.getMessage() : String.valueOf(this.error.run(var7));
            if (response.getContentType() == null) {
                response.setContentType("text/plain; charset=utf-8");
            }

            response.getWriter().print(result);
            return;
        }

        chain.doFilter(request, response);
    }
  1. 该auth方法是在SaQuickRegister类中定义的。因为/test是在配置文件中定义的,所以在执行SaRouter.match(SaQuickManager.getConfig().getInclude().split(",")).notMatch(SaQuickManager.getConfig().getExclude().split(","))后,SaRouterStaff中的isHit变量为false,所以SaRouterStaff#check()不需要执行。
	public SaRouterStaff notMatch(String... patterns) {
        if (this.isHit) {
            this.isHit = !SaRouter.isMatchCurrURI(patterns);
        }

        return this;
    }
  1. test1方法由于不在exclude配置中,所以会执行SaRouterStaff#check()。该方法中主要有两个判断,SaQuickManager.getConfig().getAuth()StpUtil.isLogin() == false。配置中的auth设置为true,所以第一个判断为true。
  2. StpUtil.isLogin()用来判断是否登录。跟踪到获取到token的方法,获取不到token,返回false,跳转到saLogin页面。
getTokenValueNotCut:249, StpLogic (cn.dev33.satoken.stp)
getTokenValue:201, StpLogic (cn.dev33.satoken.stp)
getLoginIdDefaultNull:746, StpLogic (cn.dev33.satoken.stp)
isLogin:659, StpLogic (cn.dev33.satoken.stp)
isLogin:282, StpUtil (cn.dev33.satoken.stp)
  1. 登录方法,SaQuickController#doLogin()。创造token,并且存储在客户端。
login:331, StpLogic (cn.dev33.satoken.stp)
login:293, StpLogic (cn.dev33.satoken.stp)
login:135, StpUtil (cn.dev33.satoken.stp)
doLogin:53, SaQuickController (cn.dev33.satoken.quick.web)
	public void login(Object id, SaLoginModel loginModel) {
		// 1、创建会话 
		String token = createLoginSession(id, loginModel);

		// 2、在当前客户端注入Token 
		setTokenValue(token, loginModel);
	}
  1. 存储在客户端。先保留一份在SaStorage,再存入cookie。
	public void setTokenValue(String tokenValue, SaLoginModel loginModel){
		
		if(SaFoxUtil.isEmpty(tokenValue)) {
			return;
		}
		
		// 1. 将 Token 保存到 [存储器] 里  
		setTokenValueToStorage(tokenValue);
		
		// 2. 将 Token 保存到 [Cookie] 里 
		if (getConfig().getIsReadCookie()) {
			setTokenValueToCookie(tokenValue, loginModel.getCookieTimeout());
		}
		
		// 3. 将 Token 写入到响应头里 
		if(loginModel.getIsWriteHeaderOrGlobalConfig()) {
			setTokenValueToResponseHeader(tokenValue);
		}
	}
  1. 登录的时候,从cookie中获取token,判断是否过期,如果能获取loginId,则判断是登录状态,SaTokenDaoDefaultImpl#get
	@Override
	public String get(String key) {
		clearKeyByTimeout(key);
		return (String)dataMap.get(key);
	}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值