Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Em Criar um aplicativo de chat, você aprendeu a usar APIs do WebSocket para enviar e receber dados com o Azure Web PubSub. Observe que, para simplificar, ele não requer nenhuma autenticação. Embora o Azure Web PubSub exija que um token de acesso seja conectado, a API negotiate usada no tutorial para gerar o token de acesso não precisa de autenticação. Qualquer pessoa pode chamar essa API para obter um token de acesso.
Em um aplicativo do mundo real, normalmente, o ideal é que o usuário se conecte primeiro para usar seu aplicativo. Neste tutorial, você aprenderá a integrar o Web PubSub ao sistema de autenticação e autorização do seu aplicativo para torná-lo mais seguro.
Encontre o exemplo de código completo deste tutorial no GitHub.
Neste tutorial, você aprende a:
- Habilitar a autenticação no GitHub
- Adicionar middleware de autenticação ao aplicativo
- Adicionar permissões aos clientes
Importante
As cadeias de conexão brutas aparecem neste artigo somente para fins de demonstração.
Uma cadeia de conexão inclui as informações de autorização que sua aplicação precisa para acessar o serviço Azure Web PubSub. A chave de acesso na cadeia de conexão funciona como uma senha root para o seu serviço. Em ambientes de produção, sempre proteja suas chaves de acesso. Use o Azure Key Vault para gerenciar e girar suas chaves com segurança e proteger sua conexão com WebPubSubServiceClient.
Evite distribuir chaves de acesso para outros usuários, fazer hard-coding com elas ou salvá-las em qualquer lugar em texto sem formatação que seja acessível a outras pessoas. Gire suas chaves se você acredita que elas podem ter sido comprometidas.
Adicionar autenticação ao aplicativo de sala de chat
Este tutorial reutiliza o aplicativo de chat criado em Criar um aplicativo de chat. Você também pode clonar o código de exemplo completo do aplicativo de chat no GitHub.
Neste tutorial, você adicionará a autenticação ao aplicativo de chat e o integrará ao Web PubSub.
Primeiro, adicione a autenticação do GitHub à sala de chat para que o usuário possa usar a conta do GitHub para se conectar.
Instale as dependências.
npm install --save cookie-parser npm install --save express-session npm install --save passport npm install --save passport-github2Localize o arquivo
server.jsem seu diretório e habilite a autenticação do GitHub adicionando o seguinte código aserver.js:const app = express(); const users = {}; passport.use( new GitHubStrategy({ clientID: process.argv[3], clientSecret: process.argv[4] }, (accessToken, refreshToken, profile, done) => { users[profile.id] = profile; return done(null, profile); } )); passport.serializeUser((user, done) => { done(null, user.id); }); passport.deserializeUser((id, done) => { if (users[id]) return done(null, users[id]); return done(`invalid user id: ${id}`); }); app.use(cookieParser()); app.use(session({ resave: false, saveUninitialized: true, secret: 'keyboard cat' })); app.use(passport.initialize()); app.use(passport.session()); app.get('/auth/github', passport.authenticate('github', { scope: ['user:email'] })); app.get('/auth/github/callback', passport.authenticate('github', { successRedirect: '/' }));O código anterior usa o Passport.js para habilitar a autenticação do GitHub. Esta é uma ilustração simples de como isso funciona:
-
/auth/githubredireciona para github.com para fazer login. - Depois que você entrar, GitHub redirecionará você a
/auth/github/callbackcom um código, de modo que o seu aplicativo conclua a autenticação. (Para ver como o perfil retornado pelo GitHub é verificado e armazenado no servidor, consulte o callback de verificação empassport.use().) - Após a conclusão da autenticação, você será redirecionado à home page (
/) do site.
Para obter mais detalhes sobre o OAuth do GitHub e o Passport.js, consulte os seguintes artigos:
Para testar isso, primeiro você precisa criar um aplicativo GitHub OAuth:
- Acesse https://www.github.com, abra seu perfil e selecione Configurações>Configurações do desenvolvedor.
- Acesse Aplicativos OAuth e escolha Novo Aplicativo OAuth.
- Preencha o nome do aplicativo e a URL da home page (a URL pode ser qualquer uma que desejar) e defina URL de retorno de chamada de autorização como
http://localhost:8080/auth/github/callback. Essa URL corresponde à API de callback que você expôs no seu servidor. - Depois que o aplicativo for registrado, copie a ID do cliente e selecione Gerar um segredo do cliente.
As cadeias de conexão brutas aparecem neste artigo somente para fins de demonstração. Em ambientes de produção, sempre proteja suas chaves de acesso. Use o Azure Key Vault para gerenciar e girar suas chaves com segurança e proteger sua conexão com
WebPubSubServiceClient.Execute o comando abaixo para testar as configurações; não se esqueça de substituir
<connection-string>,<client-id>e<client-secret>pelos seus valores.export WebPubSubConnectionString="<connection-string>" export GitHubClientId="<client-id>" export GitHubClientSecret="<client-secret>" node serverAgora, abra
http://localhost:8080/auth/github. Você será redirecionado para o GitHub para fazer login. Depois de se conectar, você será redirecionado ao aplicativo de chat.-
Atualize a sala de chat para usar a identidade obtida do GitHub, em vez de solicitar um nome de usuário ao usuário.
Atualize
public/index.htmlpara chamar/negotiatediretamente sem passar uma ID de usuário.let messages = document.querySelector('#messages'); let res = await fetch(`/negotiate`); if (res.status === 401) { let m = document.createElement('p'); m.innerHTML = 'Not authorized, click <a href="/auth/github">here</a> to login'; messages.append(m); return; } let data = await res.json(); let ws = new WebSocket(data.url);Quando um usuário estiver conectado, a solicitação carregará automaticamente a identidade do usuário por meio de um cookie. Então, você só precisa verificar se o usuário existe no objeto
reqe adicionar o nome de usuário ao token de acesso do Web PubSub:app.get('/negotiate', async (req, res) => { if (!req.user || !req.user.username) { res.status(401).send('missing user id'); return; } let options = { userId: req.user.username }; let token = await serviceClient.getClientAccessToken(options); res.json({ url: token.url }); });Agora, execute novamente o servidor e você verá a mensagem "Não autorizado" na primeira vez que abrir a sala de chat. Selecione o link para entrar e faça login; em seguida, você verá que funciona como antes.
Trabalhar com permissões
Nos tutoriais anteriores, você aprendeu a usar o WebSocket.send() para publicar mensagens diretamente em outros clientes usando o subprotocolo. Em um aplicativo real, talvez você não queira que o cliente faça nenhuma publicação ou assinatura em nenhum grupo sem o controle de permissão. Nesta seção, você verá como controlar os clientes usando o sistema de permissão do Web PubSub.
No Web PubSub, um cliente pode executar os seguintes tipos de operações com um subprotocolo:
- Enviar eventos para o servidor.
- Publicar mensagens em um grupo.
- Ingressar (assinar) em um grupo.
Enviar um evento para o servidor é a operação padrão do cliente. Nenhum protocolo é usado. Portanto, isso sempre é permitido. Para fazer uma publicação ou uma assinatura em um grupo, o cliente precisa obter uma permissão. Há duas maneiras pelas quais servidor concede permissão aos clientes:
- Especificar funções quando um cliente está conectado (a função é um conceito usado para representar as permissões iniciais quando um cliente está conectado).
- Usar uma API para conceder permissão a um cliente depois que ele estiver conectado.
Para obter a permissão para ingressar em um grupo, o cliente ainda precisa ingressar no grupo usando a mensagem "ingressar no grupo" depois de obter a permissão. Como alternativa, o servidor pode usar uma API para adicionar o cliente a um grupo, mesmo que ele não tenha a permissão para ingressar nele.
Agora, vamos usar esse sistema de permissão para adicionar um novo recurso à sala de chat. Você adicionará um novo tipo de usuário chamado administrador à sala de chat. Você permitirá que o administrador envie mensagens do sistema (mensagens que começam com "[SYSTEM]") diretamente no cliente.
Primeiro, você precisa separar as mensagens do sistema e do usuário em dois grupos diferentes para que possa controlar as permissões deles separadamente.
Altere server.js para enviar mensagens diferentes para grupos diferentes:
let handler = new WebPubSubEventHandler(hubName, {
path: '/eventhandler',
handleConnect: (req, res) => {
res.success({
groups: ['system', 'message'],
});
},
onConnected: req => {
console.log(`${req.context.userId} connected`);
serviceClient.group('system').sendToAll(`${req.context.userId} joined`, { contentType: 'text/plain' });
},
handleUserEvent: (req, res) => {
if (req.context.eventName === 'message') {
serviceClient.group('message').sendToAll({
user: req.context.userId,
message: req.data
});
}
res.success();
}
});
O código anterior usa WebPubSubServiceClient.group().sendToAll() para enviar a mensagem para um grupo em vez do hub.
Como a mensagem agora é enviada para grupos, você precisará adicionar clientes aos grupos para que eles possam continuar recebendo mensagens. Use o manipulador handleConnect para adicionar clientes aos grupos.
Observação
handleConnect é disparado quando um cliente tenta se conectar ao Web PubSub. Nesse manipulador, você pode retornar grupos e funções, de modo que o serviço possa adicionar uma conexão aos grupos ou conceder funções, assim que a conexão é estabelecida. O serviço também pode usar res.fail() para negar a conexão.
Para disparar handleConnect, acesse as configurações do manipulador de eventos no portal do Azure e selecione Conectar em eventos do sistema.
Você também precisa atualizar o HTML do cliente, pois agora o servidor envia mensagens JSON em vez de texto sem formatação:
let ws = new WebSocket(data.url, 'json.webpubsub.azure.v1');
ws.onopen = () => console.log('connected');
ws.onmessage = event => {
let m = document.createElement('p');
let message = JSON.parse(event.data);
switch (message.type) {
case 'message':
if (message.group === 'system') m.innerText = `[SYSTEM] ${message.data}`;
else if (message.group === 'message') m.innerText = `[${message.data.user}] ${message.data.message}`;
break;
}
messages.appendChild(m);
};
let message = document.querySelector('#message');
message.addEventListener('keypress', e => {
if (e.charCode !== 13) return;
ws.send(JSON.stringify({
type: 'event',
event: 'message',
dataType: 'text',
data: message.value
}));
message.value = '';
});
Em seguida, altere o código do cliente para envio ao grupo do sistema quando os usuários selecionarem mensagem do sistema:
<button id="system">system message</button>
...
<script>
(async function() {
...
let system = document.querySelector('#system');
system.addEventListener('click', e => {
ws.send(JSON.stringify({
type: 'sendToGroup',
group: 'system',
dataType: 'text',
data: message.value
}));
message.value = '';
});
})();
</script>
Por padrão, o cliente não tem permissão de envio a nenhum grupo. Atualize o código do servidor para conceder permissão ao usuário administrador (para simplificar, a ID do administrador é fornecida como um argumento da linha de comando).
app.get('/negotiate', async (req, res) => {
...
if (req.user.username === process.argv[2]) options.claims = { role: ['webpubsub.sendToGroup.system'] };
let token = await serviceClient.getClientAccessToken(options);
});
Agora execute node server <admin-id>. Você verá que pode enviar uma mensagem do sistema para cada cliente ao entrar como <admin-id>.
Mas se você entrar como um usuário diferente, ao selecionar mensagem do sistema, nada acontecerá. Você pode esperar que o serviço gere um erro para permitir que você saiba que a operação não é permitida. Para fornecer esses comentários, defina ackId ao publicar a mensagem. Sempre que ackId for especificado, o Web PubSub retornará uma mensagem com um ackId correspondente para indicar se a operação foi bem-sucedida ou não.
Altere o código de envio de uma mensagem do sistema para o seguinte código:
let ackId = 0;
system.addEventListener('click', e => {
ws.send(JSON.stringify({
type: 'sendToGroup',
group: 'system',
ackId: ++ackId,
dataType: 'text',
data: message.value
}));
message.value = '';
});
Altere também o código do processamento de mensagens para tratar uma mensagem ack:
ws.onmessage = event => {
...
switch (message.type) {
case 'ack':
if (!message.success && message.error.name === 'Forbidden') m.innerText = 'No permission to send system message';
break;
}
};
Agora, execute o servidor novamente e entre como outro usuário. Você verá uma mensagem de erro quando estiver tentando enviar uma mensagem do sistema.
O exemplo de código completo deste tutorial pode ser encontrado no GitHub.
Próximas etapas
Este tutorial fornece uma ideia básica de como se conectar ao serviço Web PubSub e como publicar mensagens nos clientes conectados usando o subprotocolo.
Para saber mais sobre como usar o serviço Web PubSub, leia os outros tutoriais disponíveis na documentação.