📄 iqauthhandler.java
字号:
/**
* $RCSfile$
* $Revision: 2747 $
* $Date: 2005-08-31 15:12:28 -0300 (Wed, 31 Aug 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.handler;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.QName;
import org.jivesoftware.stringprep.Stringprep;
import org.jivesoftware.stringprep.StringprepException;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.wildfire.*;
import org.jivesoftware.wildfire.auth.AuthFactory;
import org.jivesoftware.wildfire.auth.AuthToken;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.user.UserManager;
import org.jivesoftware.wildfire.user.UserNotFoundException;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.StreamError;
import java.util.ArrayList;
import java.util.List;
/**
* Implements the TYPE_IQ jabber:iq:auth protocol (plain only). Clients
* use this protocol to authenticate with the server. A 'get' query
* runs an authentication probe with a given user name. Return the
* authentication form or an error indicating the user is not
* registered on the server.<p>
*
* A 'set' query authenticates with information given in the
* authentication form. An authenticated session may reset their
* authentication information using a 'set' query.
*
* <h2>Assumptions</h2>
* This handler assumes that the request is addressed to the server.
* An appropriate TYPE_IQ tag matcher should be placed in front of this
* one to route TYPE_IQ requests not addressed to the server to
* another channel (probably for direct delivery to the recipient).
*
* @author Iain Shigeoka
*/
public class IQAuthHandler extends IQHandler implements IQAuthInfo {
private static boolean anonymousAllowed;
private Element probeResponse;
private IQHandlerInfo info;
private UserManager userManager;
private XMPPServer localServer;
private SessionManager sessionManager;
/**
* Clients are not authenticated when accessing this handler.
*/
public IQAuthHandler() {
super("XMPP Authentication handler");
info = new IQHandlerInfo("query", "jabber:iq:auth");
probeResponse = DocumentHelper.createElement(QName.get("query", "jabber:iq:auth"));
probeResponse.addElement("username");
if (AuthFactory.isPlainSupported()) {
probeResponse.addElement("password");
}
if (AuthFactory.isDigestSupported()) {
probeResponse.addElement("digest");
}
probeResponse.addElement("resource");
anonymousAllowed = "true".equals(JiveGlobals.getProperty("xmpp.auth.anonymous"));
}
public IQ handleIQ(IQ packet) throws UnauthorizedException, PacketException {
ClientSession session = sessionManager.getSession(packet.getFrom());
// If no session was found then answer an error (if possible)
if (session == null) {
Log.error("Error during authentication. Session not found in " +
sessionManager.getPreAuthenticatedKeys() +
" for key " +
packet.getFrom());
// This error packet will probably won't make it through
IQ reply = IQ.createResultIQ(packet);
reply.setChildElement(packet.getChildElement().createCopy());
reply.setError(PacketError.Condition.internal_server_error);
return reply;
}
IQ response;
try {
Element iq = packet.getElement();
Element query = iq.element("query");
Element queryResponse = probeResponse.createCopy();
if (IQ.Type.get == packet.getType()) {
String username = query.elementTextTrim("username");
if (username != null) {
queryResponse.element("username").setText(username);
}
response = IQ.createResultIQ(packet);
response.setChildElement(queryResponse);
// This is a workaround. Since we don't want to have an incorrect TO attribute
// value we need to clean up the TO attribute and send directly the response.
// The TO attribute will contain an incorrect value since we are setting a fake
// JID until the user actually authenticates with the server.
if (session.getStatus() != Session.STATUS_AUTHENTICATED) {
response.setTo((JID)null);
}
}
// Otherwise set query
else {
if (query.elements().isEmpty()) {
// Anonymous authentication
response = anonymousLogin(session, packet);
}
else {
String username = query.elementTextTrim("username");
// Login authentication
String password = query.elementTextTrim("password");
String digest = null;
if (query.element("digest") != null) {
digest = query.elementTextTrim("digest").toLowerCase();
}
// If we're already logged in, this is a password reset
if (session.getStatus() == Session.STATUS_AUTHENTICATED) {
response = passwordReset(password, packet, username, session);
}
else {
// it is an auth attempt
response = login(username, query, packet, password, session, digest);
}
}
}
}
catch (UserNotFoundException e) {
response = IQ.createResultIQ(packet);
response.setChildElement(packet.getChildElement().createCopy());
response.setError(PacketError.Condition.not_authorized);
}
catch (UnauthorizedException e) {
response = IQ.createResultIQ(packet);
response.setChildElement(packet.getChildElement().createCopy());
response.setError(PacketError.Condition.not_authorized);
}
// Send the response directly since we want to be sure that we are sending it back
// to the correct session. Any other session of the same user but with different
// resource is incorrect.
session.process(response);
return null;
}
private IQ login(String username, Element iq, IQ packet, String password, ClientSession session, String digest)
throws UnauthorizedException, UserNotFoundException {
// Verify that specified resource is not violating any string prep rule
String resource = iq.elementTextTrim("resource");
if (resource != null) {
try {
resource = Stringprep.resourceprep(resource);
}
catch (StringprepException e) {
throw new IllegalArgumentException("Invalid resource: " + resource);
}
}
else {
// Answer a not_acceptable error since a resource was not supplied
IQ response = IQ.createResultIQ(packet);
response.setChildElement(packet.getChildElement().createCopy());
response.setError(PacketError.Condition.not_acceptable);
return response;
}
username = username.toLowerCase();
// Verify that supplied username and password are correct (i.e. user authentication was successful)
AuthToken token = null;
if (password != null && AuthFactory.isPlainSupported()) {
token = AuthFactory.authenticate(username, password);
}
else if (digest != null && AuthFactory.isDigestSupported()) {
token = AuthFactory.authenticate(username, session.getStreamID().toString(),
digest);
}
if (token == null) {
throw new UnauthorizedException();
}
// Verify if there is a resource conflict between new resource and existing one.
// Check if a session already exists with the requested full JID and verify if
// we should kick it off or refuse the new connection
if (sessionManager.isActiveRoute(username, resource)) {
ClientSession oldSession;
try {
String domain = localServer.getServerInfo().getName();
oldSession = sessionManager.getSession(username, domain, resource);
oldSession.incrementConflictCount();
int conflictLimit = sessionManager.getConflictKickLimit();
if (conflictLimit != SessionManager.NEVER_KICK && oldSession.getConflictCount() > conflictLimit) {
Connection conn = oldSession.getConnection();
if (conn != null) {
// Send a stream:error before closing the old connection
StreamError error = new StreamError(StreamError.Condition.conflict);
conn.deliverRawText(error.toXML());
conn.close();
}
}
else {
IQ response = IQ.createResultIQ(packet);
response.setChildElement(packet.getChildElement().createCopy());
response.setError(PacketError.Condition.forbidden);
return response;
}
}
catch (Exception e) {
Log.error("Error during login", e);
}
}
// Set that the new session has been authenticated successfully
session.setAuthToken(token, userManager, resource);
packet.setFrom(session.getAddress());
return IQ.createResultIQ(packet);
}
private IQ passwordReset(String password, IQ packet, String username, Session session)
throws UnauthorizedException
{
IQ response;
if (password == null || password.length() == 0) {
throw new UnauthorizedException();
}
else {
try {
userManager.getUser(username).setPassword(password);
response = IQ.createResultIQ(packet);
List<String> params = new ArrayList<String>();
params.add(username);
params.add(session.toString());
Log.info(LocaleUtils.getLocalizedString("admin.password.update", params));
}
catch (UserNotFoundException e) {
throw new UnauthorizedException();
}
}
return response;
}
private IQ anonymousLogin(ClientSession session, IQ packet) {
IQ response = IQ.createResultIQ(packet);
if (anonymousAllowed) {
session.setAnonymousAuth();
response.setTo(session.getAddress());
Element auth = response.setChildElement("query", "jabber:iq:auth");
auth.addElement("resource").setText(session.getAddress().getResource());
}
else {
response.setChildElement(packet.getChildElement().createCopy());
response.setError(PacketError.Condition.forbidden);
}
return response;
}
public boolean isAnonymousAllowed() {
return anonymousAllowed;
}
public void setAllowAnonymous(boolean isAnonymous) throws UnauthorizedException {
anonymousAllowed = isAnonymous;
JiveGlobals.setProperty("xmpp.auth.anonymous", anonymousAllowed ? "true" : "false");
}
public void initialize(XMPPServer server) {
super.initialize(server);
localServer = server;
userManager = server.getUserManager();
sessionManager = server.getSessionManager();
}
public IQHandlerInfo getInfo() {
return info;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -