1. Análise de arquitetura
Atualmente, a maioria dos sistemas foi projetada com uma arquitetura de "separação de front-end e back-end", e a autenticação tradicional do modo de sessão não é mais adequada para essa arquitetura (ou muito código adicional precisa ser escrito para adaptação especial ).
Sa-Token é uma estrutura leve de autenticação de autoridade java, especialmente criada para arquiteturas de separação de front-end e back-end. Ele resolve principalmente uma série de problemas relacionados à autoridade, como autenticação de login, autenticação de autoridade, logon único, OAuth2 e autenticação de gateway de microsserviço.
Gitee endereço de código aberto: https://gitee.com/dromara/sa-token
Este artigo apresentará os projetos de separação de front-end e back-end sob a arquitetura Springboot e como usar o Sa-Token para concluir a autenticação de login de maneira conveniente.
Primeiro introduza a dependência Sa-Token no projeto:
<!-- Sa-Token 权限认证 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.34.0</version>
</dependency>
Observação: se você estiver usando SpringBoot 3.x
, basta sa-token-spring-boot-starter
alterar para sa-token-spring-boot3-starter
.
2. Sem modo de cookie
Sem modo de cookie: Refere-se especificamente a terminais que não suportam a função de cookie. De um modo geral, é o que costumamos dizer - o modo de separação front-end e back-end .
Os métodos convencionais de autenticação no lado da Web geralmente são Cookie模式
preenchidos por , e os cookies têm duas características:
- Gravável pelo controle de back-end.
- Enviado automaticamente em cada solicitação.
Isso nos permite concluir todo o processo de autenticação sem nenhuma operação especial no código front-end (porque todo o processo é concluído pelo controle back-end)
Para a função de cookies, a maioria das pessoas ficará confusa neste momento, como realizar a autenticação?
Veja os truques, a resposta é realmente muito simples:
- Se o back-end não puder controlar a escrita, o front-end grava sozinho. (A dificuldade está em como o backend passa o Token para o frontend )
- Se cada solicitação não puder ser enviada automaticamente, envie-a manualmente. (A dificuldade está em como o front-end passa o token para o back-end e o back-end o lê ao mesmo tempo )
3. O back-end retorna o token para o front-end
- Primeira chamada
StpUtil.login(id)
para fazer login. - Call
StpUtil.getTokenInfo()
Retorna os parâmetros detalhados do token da sessão atual.- Este método retorna um objeto com duas propriedades principais:
tokenName
etokenValue
(o nome do token e o valor do token). - Passe esse objeto para o primeiro plano e deixe o pessoal do front-end salvar esses dois valores localmente.
- Este método retorna um objeto com duas propriedades principais:
Exemplo de código:
// 登录接口
@RequestMapping("doLogin")
public SaResult doLogin() {
// 第1步,先登录上
StpUtil.login(10001);
// 第2步,获取 Token 相关参数
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
// 第3步,返回给前端
return SaResult.data(tokenInfo);
}
4. O front-end envia o token para o back-end
- Seja um aplicativo ou um pequeno programa, os métodos de entrega são semelhantes.
- Ou seja, insira o token na requisição
header
no formato:{tokenName: tokenValue}
. - Pegue o uni-app clássico de framework cross-end como exemplo:
Método 1, simples e rude
// 1、首先在登录时,将 tokenValue 存储在本地,例如:
uni.setStorageSync('tokenValue', tokenValue);
// 2、在发起ajax请求的地方,获取这个值,并塞到header里
uni.request({
url: 'https://www.example.com/request', // 仅为示例,并非真实接口地址。
header: {
"content-type": "application/x-www-form-urlencoded",
"satoken": uni.getStorageSync('tokenValue') // 关键代码, 注意参数名字是 satoken
},
success: (res) => {
console.log(res.data);
}
});
Método 2, mais flexível
// 1、首先在登录时,将tokenName和tokenValue一起存储在本地,例如:
uni.setStorageSync('tokenName', tokenName);
uni.setStorageSync('tokenValue', tokenValue);
// 2、在发起ajax的地方,获取这两个值, 并组织到head里
var tokenName = uni.getStorageSync('tokenName'); // 从本地缓存读取tokenName值
var tokenValue = uni.getStorageSync('tokenValue'); // 从本地缓存读取tokenValue值
var header = {
"content-type": "application/x-www-form-urlencoded"
};
if (tokenName != undefined && tokenName != '') {
header[tokenName] = tokenValue;
}
// 3、后续在发起请求时将 header 对象塞到请求头部
uni.request({
url: 'https://www.example.com/request', // 仅为示例,并非真实接口地址。
header: header,
success: (res) => {
console.log(res.data);
}
});
- Desde que
token
o valor seja passado para o back-end dessa maneira, o Sa-Token pode ler automaticamente o valor do token para autenticação, assim como o lado do PC tradicional. - Você pode ter dúvidas, eu tenho que
ajax
escrever uma pilha dessas toda vez? Não é problemático?- Claro, você não pode escrever tal pilha de cada ajax, porque este tipo de código repetitivo deve ser encapsulado em uma função e chamado uniformemente.
Outras soluções:
Se você conhece muito bem sobre cookies, entenderá que o chamado cookie é essencialmente um header
parâmetro especial
e, como é apenas um parâmetro de cabeçalho, podemos simulá-lo e implementá-lo manualmente para concluir a operação de autenticação.
Na verdade, esta é 无Cookie模式
outra solução correta. Os alunos interessados podem descobrir isso no Baidu, então não entrarei em detalhes aqui.
5. Comparação de código
Para mostrar de forma mais intuitiva a diferença entre a arquitetura integrada de front-end e back-end e a arquitetura de separação de front-end e back-end, aqui está outro exemplo:
package com.pj.cases.up;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
/** * Sa-Token 前后端分离模式示例 * * @author kong * @since 2022-10-17 */
@RestController
@RequestMapping("/NotCookie/")
public class NotCookieController {
// 前后端一体模式的登录样例 ---- http://localhost:8081/NotCookie/doLogin?name=zhang&pwd=123456
@RequestMapping("doLogin")
public SaResult doLogin(String name, String pwd) {
if("zhang".equals(name) && "123456".equals(pwd)) {
// 会话登录
StpUtil.login(10001);
return SaResult.ok();
}
return SaResult.error("登录失败");
}
// 前后端分离模式的登录样例 ---- http://localhost:8081/NotCookie/doLogin2?name=zhang&pwd=123456
@RequestMapping("doLogin2")
public SaResult doLogin2(String name, String pwd) {
if("zhang".equals(name) && "123456".equals(pwd)) {
// 会话登录
StpUtil.login(10001);
// 与常规登录不同点之处:这里需要把 Token 信息从响应体中返回到前端
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
return SaResult.data(tokenInfo);
}
return SaResult.error("登录失败");
}
}
- Interface 1: O token será devolvido ao front-end no contexto do cookie e será enviado automaticamente pelo navegador sempre que solicitado, este modo é adequado para a arquitetura integrada de front-end e back-end.
- Interface 2: O token será devolvido ao front-end no corpo da resposta e será armazenado manualmente pelo front-end e enviado manualmente a cada solicitação. Este modo é adequado para a arquitetura em que o front-end e o back-end são separados.
6. Personalize o prefixo enviado pelo Token
Em alguns sistemas, quando o front-end enviar o token, um prefixo fixo será adicionado na frente, por exemplo:
{
"satoken": "Bearer xxxx-xxxx-xxxx-xxxx"
}
Neste momento, se o back-end não fizer nenhum processamento especial, a estrutura o considerará Bearer
como parte do token e não poderá ler as informações do token normalmente, resultando em falha de autenticação.
Para fazer isso, precisamos adicionar a seguinte configuração ao yml:
sa-token:
# token前缀
token-prefix: Bearer
Neste momento, o Sa-Token pode ser cortado ao ler o Token Bearer
e obtido com sucesso xxxx-xxxx-xxxx-xxxx
.
ponto importante:
- Deve haver um espaço entre o prefixo do token e o valor do token.
- Uma vez configurado o prefixo do Token,
Token
ele deve ser enviado com o prefixo, caso contrário o framework não conseguirá ler o Token. - Como
Cookie
os caracteres de espaço não podem ser armazenados em , significa que após a configuração do prefixo do Token, o método de autenticação do Cookie será inválido. Neste momento, o Token só pode ser enviado para transmissãoheader
.
7. Estilo de token personalizado
A estratégia de geração de token padrão do Sa-Token é o estilo uuid, que se parece com: 623368f0-ae5e-4475-a53f-93e4225f16ae
.
Se você não estiver muito interessado neste estilo, também pode definir a geração de token para outros estilos.
Como configurá-lo? Você só precisa configurá-lo no arquivo de configuração yml sa-token.token-style=风格类型
, que possui vários valores:
// 1. token-style=uuid —— uuid风格 (默认风格)
"623368f0-ae5e-4475-a53f-93e4225f16ae"
// 2. token-style=simple-uuid —— 同上,uuid风格, 只不过去掉了中划线
"6fd4221395024b5f87edd34bc3258ee8"
// 3. token-style=random-32 —— 随机32位字符串
"qEjyPsEA1Bkc9dr8YP6okFr5umCZNR6W"
// 4. token-style=random-六十四 —— 随机六十四位字符串
"v4ueNLEpPwMtmOPMBtOOeIQsvP8z9gkMgIVibTUVjkrNrlfra5CGwQkViDjO8jcc"
// 5. token-style=random-128 —— 随机128位字符串
"nojYPmcEtrFEaN0Otpssa8I8jpk8FO53UcMZkCP9qyoHaDbKS6dxoRPky9c6QlftQ0pdzxRGXsKZmUSrPeZBOD6kJFfmfgiRyUmYWcj4WU4SSP2ilakWN1HYnIuX0Olj"
// 6. token-style=tik —— tik风格
"gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__"
8. Estratégia de geração de token personalizado
Se você acha que nenhum dos estilos acima é o seu favorito, também pode personalizar a estratégia de geração de token para personalizar o estilo de geração de token.
Como fazer isso? Você só precisa reescrever o algoritmo SaStrategy
da classe de estratégia createToken
:
As etapas de referência são as seguintes:
1. SaTokenConfigure
Adicione o código na classe de configuração:
@Configuration
public class SaTokenConfigure {
/** * 重写 Sa-Token 框架内部算法策略 */
@Autowired
public void rewriteSaStrategy() {
// 重写 Token 生成策略
SaStrategy.me.createToken = (loginId, loginType) -> {
return SaFoxUtil.getRandomString(60); // 随机60位长度字符串
};
}
}
2. Chame StpUtil.login(10001)
o método novamente para efetuar login e observe o estilo do token gerado:
gfuPSwZsnUhwgz08GTCH4wOgasWtc3odP4HLwXJ7NDGOximTvT4OlW19zeLH
Referências
- Declaração Sa-Token: https://sa-token.cc
- Endereço do armazém Gitee: https://gitee.com/dromara/sa-token
- Endereço do armazém GitHub: https://github.com/dromara/sa-token