I found this guide for developing your own Server Authentication Module (SAM) for Glassfish: http://docs.oracle.com/cd/E18930_01/html/821-2418/gizel.html
It seems pretty straightforward to verify some credentials (in HTTP Auth headers for instance), but my question is this:
Can I develop my SAM in such a way that I can forward the user to a specific page if he's not logged in?
Here's the example from the guide:
package tip.sam;
import java.io.IOException;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.AuthStatus;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.MessagePolicy;
import javax.security.auth.message.callback.CallerPrincipalCallback;
import javax.security.auth.message.callback.GroupPrincipalCallback;
import javax.security.auth.message.callback.PasswordValidationCallback;
import javax.security.auth.message.module.ServerAuthModule;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.util.Base64;
public class MySam implements ServerAuthModule {
protected static final Class[]
supportedMessageTypes = new Class[]{
HttpServletRequest.class,
HttpServletResponse.class
};
private MessagePolicy requestPolicy;
private MessagePolicy responsePolicy;
private CallbackHandler handler;
private Map options;
private String realmName = null;
private String defaultGroup[] = null;
private static final String REALM_PROPERTY_NAME =
"realm.name";
private static final String GROUP_PROPERTY_NAME =
"group.name";
private static final String BASIC = "Basic";
static final String AUTHORIZATION_HEADER =
"authorization";
static final String AUTHENTICATION_HEADER =
"WWW-Authenticate";
public void initialize(MessagePolicy reqPolicy,
MessagePolicy resPolicy,
CallbackHandler cBH, Map opts)
throws AuthException {
requestPolicy = reqPolicy;
responsePolicy = resPolicy;
handler = cBH;
options = opts;
if (options != null) {
realmName = (String)
options.get(REALM_PROPERTY_NAME);
if (options.containsKey(GROUP_PROPERTY_NAME)) {
defaultGroup = new String[]{(String)
options.get(GROUP_PROPERTY_NAME)};
}
}
}
public Class[] getSupportedMessageTypes() {
return supportedMessageTypes;
}
public AuthStatus validateRequest(
MessageInfo msgInfo, Subject client,
Subject server) throws AuthException {
try {
String username =
processAuthorizationToken(msgInfo, client);
if (username ==
null && requestPolicy.isMandatory()) {
return sendAuthenticateChallenge(msgInfo);
}
setAuthenticationResult(
username, client, msgInfo);
return AuthStatus.SUCCESS;
} catch (Exception e) {
AuthException ae = new AuthException();
ae.initCause(e);
throw ae;
}
}
private String processAuthorizationToken(
MessageInfo msgInfo, Subject s)
throws AuthException {
HttpServletRequest request =
(HttpServletRequest)
msgInfo.getRequestMessage();
String token =
request.getHeader(AUTHORIZATION_HEADER);
if (token != null && token.startsWith(BASIC + " ")) {
token = token.substring(6).trim();
// Decode and parse the authorization token
String decoded =
new String(Base64.decode(token.getBytes()));
int colon = decoded.indexOf(':');
if (colon <= 0 || colon == decoded.length() - 1) {
return (null);
}
String username = decoded.substring(0, colon);
// use the callback to ask the container to
// validate the password
PasswordValidationCallback pVC =
new PasswordValidationCallback(s, username,
decoded.substring(colon + 1).toCharArray());
try {
handler.handle(new Callback[]{pVC});
pVC.clearPassword();
} catch (Exception e) {
AuthException ae = new AuthException();
ae.initCause(e);
throw ae;
}
if (pVC.getResult()) {
return username;
}
}
return null;
}
private AuthStatus sendAuthenticateChallenge(
MessageInfo msgInfo) {
String realm = realmName;
// if the realm property is set use it,
// otherwise use the name of the server
// as the realm name.
if (realm == null) {
HttpServletRequest request =
(HttpServletRequest)
msgInfo.getRequestMessage();
realm = request.getServerName();
}
HttpServletResponse response =
(HttpServletResponse)
msgInfo.getResponseMessage();
String header = BASIC + " realm=\"" + realm + "\"";
response.setHeader(AUTHENTICATION_HEADER, header);
response.setStatus(
HttpServletResponse.SC_UNAUTHORIZED);
return AuthStatus.SEND_CONTINUE;
// MAYBE SOMETHING HERE?
}
public AuthStatus secureResponse(
MessageInfo msgInfo, Subject service)
throws AuthException {
return AuthStatus.SEND_SUCCESS;
}
public void cleanSubject(MessageInfo msgInfo,
Subject subject)
throws AuthException {
if (subject != null) {
subject.getPrincipals().clear();
}
}
private static final String AUTH_TYPE_INFO_KEY =
"javax.servlet.http.authType";
// distinguish the caller principal
// and assign default groups
private void setAuthenticationResult(String name,
Subject s, MessageInfo m)
throws IOException,
UnsupportedCallbackException {
handler.handle(new Callback[]{
new CallerPrincipalCallback(s, name)
});
if (name != null) {
// add the default group if the property is set
if (defaultGroup != null) {
handler.handle(new Callback[]{
new GroupPrincipalCallback(s, defaultGroup)
});
}
m.getMap().put(AUTH_TYPE_INFO_KEY, ""MySAM");
}
}
}
Yes, you can do that in the validateRequest method.
Here is a simple example:
public AuthStatus validateRequest(MessageInfo messageInfo,
Subject clientSubject,
Subject serviceSubject) throws AuthException {
// clientSubject.getPrincipals() returns the principals
// check this set to know if the user is not logged in
// if the user is not logged in do the following
HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage();
response.sendRedirect("login.html");
}
It might be better to do it inside of a custom LoginModule (if you already know what that is), but I guess this depends on your requirements.
See also:
LoginModule Bridge Profile (JASPIC) in glassfish
Implementing container authentication in Java EE with JASPIC
JAAS for human beings
Related
I have my Web Application up and running in tomcat. Now, I have to run my application(war file) inside WebLogic 12c.
I could not find sufficient information for:
1) Is weblogic.xml mandatory along with web.xml inside war file? Tutorials are mentioning about setting the context and some more configurations in weblogic.xml but no where I could figure out that its mandatory.
2) I need to configure JAAS realm inside WebLogic. I tried configuring the realm but dont know something is screwed with the configurations. Can someone point to me proper tutorial or provide the steps required for JAAS setup.
I added -Djava.security.auth.login.config=%DOMAIN_HOME%\jaas.config inside startWebLogic.cmd file.
Below is my login module code:
public class AuthLoginModule implements LoginModule {
private static Logger logger = Logger.getLogger(AuthLoginModule.class);
// initial state
private Subject subject;
private CallbackHandler callbackHandler;
private Map<String, ?> sharedState;
private Map<String, ?> options;
// the authentication status
private boolean succeeded = false;
private boolean commitSucceeded = false;
// username and password
private String username;
private String password;
Map<String,String> userData = new HashMap<String,String>();
private AuthPrincipal userPrincipal;
public AuthLoginModule() throws WebAuthServiceException {
super();
}
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
this.setSharedState(sharedState);
this.setOptions(options);
String appName = options.get(WebAuthConstants.APP_UNIQUE_NAME).toString();
logger.info("AppName in AuthLoginModule: " + appName);
}
public boolean login() throws LoginException {
if (callbackHandler == null)
throw new LoginException("Error: no CallbackHandler available " + "to garner authentication information from the user");
Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback("user name: ");
callbacks[1] = new PasswordCallback("password: ", false);
try {
callbackHandler.handle(callbacks);
username = ((NameCallback) callbacks[0]).getName();
char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();
if (tmpPassword == null) {
// treat a NULL password as an empty password
tmpPassword = new char[0];
}
password = new String(tmpPassword);
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
throw new LoginException("User name or password is empty");
}
} catch (java.io.IOException ioe) {
throw new LoginException(ioe.toString());
} catch (UnsupportedCallbackException uce) {
throw new LoginException("Error: " + uce.getCallback().toString() + " not available to garner authentication information " + "from the user");
}
String validateUserCredData = validateUserCred();
if (validateUserCredData!=null) {
if(JsonUtil.jsonFromString(validateUserCredData).get("statusCode").getAsInt()== HttpStatus.SC_UNAUTHORIZED) {
userData.put(DataConstants._USER_PWD_STATUS, DataConstants._RESET_USER_PWD);
}
succeeded = true;
} else {
succeeded = false;
}
return succeeded;
}
private String validateUserCred() {
try {
logger.info("Started validating user credentials for: " + username);
// If there is no error then user allowed to access all
UserClientService UserClientService = ClientServiceFactory.getInstance().getUserService();
return UserClientService.validateUserCredentials(username, password);
} catch (Throwable e) {
logger.error("Exception while authentication user against Service API, Error Code: ", e);
}
return null;
}
public boolean commit() throws LoginException {
if (succeeded == false) {
return false;
} else {
// add a Principal (authenticated identity) to the Subject
// assume the user we authenticated is the SamplePrincipal
userPrincipal = new AuthPrincipal(username, password, userData);
if (!subject.getPrincipals().contains(userPrincipal))
subject.getPrincipals().add(userPrincipal);
logger.info("Login Module successfully added user principal");
// in any case, clean out state
username = null;
password = null;
commitSucceeded = true;
return true;
}
}
public boolean abort() throws LoginException {
if (succeeded == false) {
return false;
} else if (succeeded == true && commitSucceeded == false) {
// login succeeded but overall authentication failed
succeeded = false;
username = null;
password = null;
userPrincipal = null;
} else {
// overall authentication succeeded and commit succeeded,
// but someone else's commit failed
logout();
}
return true;
}
public boolean logout() throws LoginException {
subject.getPrincipals().remove(userPrincipal);
succeeded = false;
succeeded = commitSucceeded;
username = null;
password = null;
userPrincipal = null;
logger.info("Login Module successfully removed user principal after successful logout");
return true;
}
public Map<String, ?> getSharedState() {
return sharedState;
}
public void setSharedState(Map<String, ?> sharedState) {
this.sharedState = sharedState;
}
public Map<String, ?> getOptions() {
return options;
}
public void setOptions(Map<String, ?> options) {
this.options = options;
}
}
In some of the tutorials, I could see, LoginModule written specific to WebLogic, but I feel LoginModule should not change for any server as it follows J2EE.
Any help would be appreciated.
Finally,I made Form Based JAAS configuration to work as per my needs.
I followed the oracle docs link provided by KC Wong in the comments section and some blogs as well.
Below are the steps that I followed.
1) First I need to create a Custom Authentication Provider. I created a Java Project for that which includes my LoginModule, AuthenticationProviderImpl(which implements AuthenticationProviderV2) and MBean XML.
This MBean XML contains the information about the MBeanType and MBeanAttribute.
I posted some important files below for more information.
WLAuthenticationProviderImpl .java
package com.abc.wls.security.providers.authentication;
import java.util.HashMap;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
import weblogic.management.security.ProviderMBean;
import weblogic.security.provider.PrincipalValidatorImpl;
import weblogic.security.spi.AuthenticationProviderV2;
import weblogic.security.spi.IdentityAsserterV2;
import weblogic.security.spi.PrincipalValidator;
import weblogic.security.spi.SecurityServices;
public class WLAuthenticationProviderImpl implements AuthenticationProviderV2 {
private String description = "MyOwn WLAuthentication Provider";
// private SimpleSampleAuthenticatorDatabase database;
private LoginModuleControlFlag controlFlag;
public void initialize(ProviderMBean mbean, SecurityServices services) {
System.out.println("WLAuthenticationProviderImpl.initialize");
// WLAuthenticationProviderMBean mbean =
// (WLAuthenticationProviderMBean) mbean;
// SimpleSampleAuthenticatorMBean myMBean =
// (SimpleSampleAuthenticatorMBean) mbean;
// description = myMBean.getDescription() + "\n" + myMBean.getVersion();
// database = new SimpleSampleAuthenticatorDatabase(myMBean);
// String flag = myMBean.getControlFlag();
/*
* if (flag.equalsIgnoreCase("REQUIRED")) { controlFlag =
* LoginModuleControlFlag.REQUIRED; } else if
* (flag.equalsIgnoreCase("OPTIONAL")) { controlFlag =
* LoginModuleControlFlag.OPTIONAL; } else if
* (flag.equalsIgnoreCase("REQUISITE")) { controlFlag =
* LoginModuleControlFlag.REQUISITE; } else if
* (flag.equalsIgnoreCase("SUFFICIENT")) { controlFlag =
* LoginModuleControlFlag.SUFFICIENT; } else { throw new
* IllegalArgumentException("invalid flag value" + flag); }
*/
}
public String getDescription() {
System.out.println("WLAuthenticationProviderImpl.getDescription");
return description;
}
public void shutdown() {
System.out.println("WLSecurityProviderImpl.shutdown");
}
private AppConfigurationEntry getConfiguration(HashMap options) {
System.out.println("WLAuthenticationProviderImpl.getConfiguration");
if (options == null)
options = new HashMap<>();
options.put("app-unique-name", "xyz-ui");
// return new
// AppConfigurationEntry("examples.security.providers.authentication.Simple.Simple.SampleLoginModuleImpl",
// controlFlag, options);
return new AppConfigurationEntry("com.abc.wls.security.providers.authentication.WLServerLoginModule", LoginModuleControlFlag.REQUIRED, options);
}
public AppConfigurationEntry getLoginModuleConfiguration() {
System.out.println("WLAuthenticationProviderImpl.getLoginModuleConfiguration");
HashMap options = new HashMap();
return getConfiguration(options);
}
public AppConfigurationEntry getAssertionModuleConfiguration() {
System.out.println("WLAuthenticationProviderImpl.getAssertionModuleConfiguration");
HashMap options = new HashMap();
options.put("IdentityAssertion", "true");
return getConfiguration(options);
}
public PrincipalValidator getPrincipalValidator() {
return new PrincipalValidatorImpl();
}
public IdentityAsserterV2 getIdentityAsserter() {
return null;
}
}
WLServerLoginModule.java
package com.abc.wls.security.providers.authentication;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import weblogic.logging.NonCatalogLogger;
import weblogic.security.principal.WLSGroupImpl;
import weblogic.security.principal.WLSUserImpl;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class WLServerLoginModule implements LoginModule {
private static NonCatalogLogger logger = new NonCatalogLogger("WLServerLoginModule");
// initial state
private Subject subject;
private CallbackHandler callbackHandler;
private Map<String, ?> sharedState;
private Map<String, ?> options;
// the authentication status
private boolean succeeded = false;
private boolean commitSucceeded = false;
// username and password
private String username;
private String password;
Map<String, String> userData = new HashMap<String, String>();
private final JsonParser jsonParser = new JsonParser();
private WLSAuthPrincipal userPrincipal;
public WLServerLoginModule() throws LoginException {
super();
}
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
logger.info("WLServerLoginModule.initialize");
this.subject = subject;
this.callbackHandler = callbackHandler;
this.setSharedState(sharedState);
this.setOptions(options);
String appName = options.get("app-unique-name").toString();
logger.info("AppName in WLServerLoginModule: " + appName);
}
public boolean login() throws LoginException {
logger.info("WLServerLoginModule.login");
if (callbackHandler == null)
throw new LoginException("Error: no CallbackHandler available " + "to garner authentication information from the user");
Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback("user name: ");
callbacks[1] = new PasswordCallback("password: ", false);
try {
callbackHandler.handle(callbacks);
username = ((NameCallback) callbacks[0]).getName();
char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();
if (tmpPassword == null) {
// treat a NULL password as an empty password
tmpPassword = new char[0];
}
password = new String(tmpPassword);
if (isEmpty(username) || isEmpty(password)) {
throw new LoginException("User name or password is empty");
}
} catch (java.io.IOException ioe) {
throw new LoginException(ioe.toString());
} catch (UnsupportedCallbackException uce) {
throw new LoginException("Error: " + uce.getCallback().toString() + " not available to garner authentication information " + "from the user");
}
try {
if (isValidUser(username, password)) {
succeeded = true;
} else {
succeeded = false;
}
} catch (Exception e) {
logger.error("Post validation exception e: ", e);
succeeded = false;
}
return succeeded;
}
private boolean isValidUser(String username, String password) {
// Your custom validation logic
return true;
}
public boolean commit() throws LoginException {
logger.info("WLServerLoginModule.commit");
if (succeeded == false) {
return false;
} else {
// add a Principal (authenticated identity) to the Subject
// assume the user we authenticated is the SamplePrincipal
userPrincipal = new WLSAuthPrincipal(username, password, userData);
if (!subject.getPrincipals().contains(userPrincipal)) {
// subject.getPrincipals().add(new WLSUserImpl(username));
subject.getPrincipals().add(userPrincipal);
logger.info("Custom User principal Added");
}
subject.getPrincipals().add(new WLSUserImpl(username));
subject.getPrincipals().add(new WLSGroupImpl("ABC_USERS"));
logger.info("Login Module successfully added user principal");
if (subject != null && subject.getPrincipals() != null) {
logger.info("All user principals added: " + subject.getPrincipals());
logger.info("All user principals count: " + subject.getPrincipals().size());
}
// in any case, clean out state
username = null;
password = null;
commitSucceeded = true;
return true;
}
}
public boolean abort() throws LoginException {
logger.info("WLServerLoginModule.abort");
if (succeeded == false) {
return false;
} else if (succeeded == true && commitSucceeded == false) {
// login succeeded but overall authentication failed
succeeded = false;
username = null;
password = null;
userPrincipal = null;
} else {
// overall authentication succeeded and commit succeeded, but
// someone else's commit failed
logout();
}
return true;
}
public boolean logout() throws LoginException {
logger.info("WLServerLoginModule.logout");
subject.getPrincipals().remove(userPrincipal);
succeeded = false;
succeeded = commitSucceeded;
username = null;
password = null;
userPrincipal = null;
logger.info("Login Module successfully removed user principal after successful logout");
return true;
}
public Map<String, ?> getSharedState() {
return sharedState;
}
public void setSharedState(Map<String, ?> sharedState) {
this.sharedState = sharedState;
}
public Map<String, ?> getOptions() {
return options;
}
public void setOptions(Map<String, ?> options) {
this.options = options;
}
}
2) I wanted to have my own Custom AuthPrincipal which extends to WLSAbstractPrincipal and implements WLUser. My case is slightly different, I wanted to store username, password and some more critical information about the user in the AuthPrincipal. So, I create this Custom AuthPrincipal.
3) Now, create a build.xml which will emit this Custom Authentication Principal.
4) After generating jar out of this, I copied it inside {WL_HOME}/server/lib/mbeantypes along with the default Authentication Providers supported by WebLogic.
5) Now, We need to change the default realm that is myrealm. Create new Authentication Provider with any name and select Type of it as the name of the custom Auth Provider that you have created. And make this Auth Provider as REQUIRED one.
I made a rest service using Jersey/Jackson and I'm deploying to Tomcat 7 in dev. Locally, using Jetty, when I make the call
https://localhost/usorgws-web/v1/usorg/search/dep?page=2&limit=10
everything works fine and I get a 200 response with the return payload being a list of objects. But when I deploy to our dev environment and make the same call replacing localhost with the server's hostname, I get a 200 Okay, but no payload is returned. I'm sure that it's working because I'm putting print lines in the code and I'm watching the logs. I can see the payload as a populated list propagate from my dao all the way up to my resource. But it looks like as soon as the tomcat container gets a hold of the Response, it strips the payload for some reason. I don't see any errors in the logs
Here is some code:
Resource
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.List;
/**
* Created by barryj on 9/14/16.
*/
#Path("/usorg")
#Component
public class USOrgResource {
private final static Logger logger = Logger.getLogger(USOrgResource.class);
#Autowired
private USOrgDao usOrgDao;
#Autowired
private USOrgService usOrgService;
#Context
private UriInfo uriInfo;
#Value("${pagination.limit.default.min}")
private int minLimit;
#Value("${pagination.limit.default.max}")
private int maxLimit;
#GET
#Path("orgCode/{orgCode}")
#Produces(MediaType.APPLICATION_JSON)
public Response getUSOrgByOrgCode(#PathParam("orgCode") String orgCode) throws
SystemException {
USOrg usOrg = null;
try {
usOrg = usOrgDao.getUsOrgByOrgCode(orgCode);
} catch (DataAccessException e) {
logger.error(e.getMessage(), e);
throw new SystemException();
}
ResponseWrapper<USOrg> responseWrapper = new ResponseWrapper<>();
responseWrapper.setData(usOrg);
responseWrapper.setDescription("USOrg retrieval by org_code");
responseWrapper.setSuccess(true);
return Response.status(200).entity(responseWrapper).build();
}
#GET
#Path("search/{term}")
#Produces(MediaType.APPLICATION_JSON)
public Response searchUsOrgWithPagination(#PathParam("term") String term, #QueryParam
("page") int page, #QueryParam("limit") int limit) throws SystemException {
logger.info("hit");
checkPageBounds(limit);
USOrgPageResults usOrgPageResults = null;
try {
usOrgPageResults = usOrgService.searchUsOrg(term, page, limit);
} catch (DataAccessException e) {
logger.error(e.getMessage(), e);
throw new SystemException();
}
ResponseWrapper<List<USOrg>> responseWrapper = new ResponseWrapper<>();
responseWrapper.setData(usOrgPageResults.getUsOrgList());
responseWrapper.setDescription("USOrg search");
responseWrapper.setSuccess(true);
return buildPaginationResponse(200, responseWrapper, usOrgPageResults);
}
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getUSOrg(#QueryParam("page") int page, #QueryParam("limit") int limit) throws
SystemException {
logger.info("hit");
checkPageBounds(limit);
USOrgPageResults usOrgPageResults = null;
try {
usOrgPageResults = usOrgService.getUsOrg(page, limit);
} catch (DataAccessException e) {
logger.error(e.getMessage(), e);
throw new SystemException();
}
ResponseWrapper<List<USOrg>> responseWrapper = new ResponseWrapper<>();
responseWrapper.setData(usOrgPageResults.getUsOrgList());
responseWrapper.setDescription("USOrg search");
responseWrapper.setSuccess(true);
if (responseWrapper.getData() != null)
logger.info("list size: " + responseWrapper.getData().size());
else
logger.info("list is null");
return buildPaginationResponse(200, responseWrapper, usOrgPageResults);
}
private Response buildPaginationResponse(int status, Object entity, USOrgPageResults
usOrgPageResults) {
final String PAGE = "?page=";
final String LIMIT = "&limit=";
String first = "";
if (usOrgPageResults.getFirst() != null)
first = uriInfo.getAbsolutePath() + PAGE + usOrgPageResults.getFirst().getPage() +
LIMIT + usOrgPageResults.getFirst().getLimit();
String prev = "";
if (usOrgPageResults.getSelf().getPage() > 0)
prev = uriInfo.getAbsolutePath() + PAGE + usOrgPageResults.getPrev()
.getPage() + LIMIT + usOrgPageResults.getPrev().getLimit();
String self = uriInfo.getAbsolutePath() + PAGE + usOrgPageResults.getSelf().getPage() +
LIMIT + usOrgPageResults.getSelf().getLimit();
String next = "";
if (usOrgPageResults.getNext() != null)
next = uriInfo.getAbsolutePath() + PAGE + usOrgPageResults.getNext().getPage() +
LIMIT + usOrgPageResults.getNext().getLimit();
String last = "";
if (usOrgPageResults.getLast() != null)
last = uriInfo.getAbsolutePath() + PAGE + usOrgPageResults.getLast().getPage() +
LIMIT + usOrgPageResults.getLast().getLimit();
return Response.status(status).entity(entity).header("first", first).header
("prev", prev).header("self", self).header("next", next).header
("last", last).build();
}
private void checkPageBounds(int limit) {
if (limit < minLimit)
throw new NotAllowedException("The limit requested is below the default limit minimum");
if (limit > maxLimit)
throw new NotAllowedException("The limit requested is above the default limit maximum");
}
public void setMinLimit(int minLimit) {
this.minLimit = minLimit;
}
public void setMaxLimit(int maxLimit) {
this.maxLimit = maxLimit;
}
}
Here is the WebApplication class where I register my JacksonJaxbJsonProvider
import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
import org.glassfish.jersey.jackson.JacksonFeature;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;
public class WebApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> s = new HashSet<Class<?>>();
s.add(USOrgResource.class);
s.add(JacksonFeature.class);
s.add(SystemExceptionMapper.class);
s.add(NotFoundExceptionMapper.class);
s.add(NotAllowedExceptionMapper.class);
s.add(JacksonJaxbJsonProvider.class);
s.add(CommonResponseHeaderFilter.class);
return s;
}
}
I haven't gotten to the bottom of it yet, but when I comment out the line s.add(CommonResponseHeaderFilter.class) in WebApplication, everything works fine when I deploy to tomcat
public class CommonResponseHeaderFilter implements ContainerResponseFilter {
public CommonResponseHeaderFilter() {
}
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
responseContext.getHeaders().add("x-content-type-options", "nosniff");
responseContext.getHeaders().add("Cache-Control", "no-cache");
responseContext.getHeaders().add("Keep-Alive", "timeout=5, max=100");
responseContext.getHeaders().add("Connection", "Keep-Alive");
responseContext.getHeaders().add("Transfer-Encoding", "chunked");
}
}
I am trying to configure a simple custom authentication filter that checks for a token on every page of the web app except the '/login' page. Right now, the filter is up and running, but no matter what settings I change, the filter is being called on every page, including '/login' which I have set to permitAll().
When I access localhost:8080/login, I expect it to not call this filter based on my configuration below, but instead it throws an exception in the filter because no session is found.
My question is how do I limit the filter to all pages except the '/login' page?
Here is my config:
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
private UserDetailsService userDetailsService;
private PreAuthenticatedAuthenticationProvider preAuthenticatedProvider;
public SecurityConfig() {
super();
userDetailsService = new UserDetailsServiceImpl();
UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> wrapper =
new UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>(userDetailsService);
preAuthenticatedProvider = new PreAuthenticatedAuthenticationProvider();
preAuthenticatedProvider.setPreAuthenticatedUserDetailsService(wrapper);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(preAuthenticatedProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
OpenTokenRequestAuthenticationFilter filter = new OpenTokenRequestAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
http
.addFilter(filter)
.authorizeRequests()
.antMatchers("/login").permitAll();
}
}
And here is the filter:
public class OpenTokenRequestAuthenticationFilter extends
AbstractPreAuthenticatedProcessingFilter {
/**
* logger for the class
*/
private static final Logger logger = LoggerFactory.getLogger(OpenTokenRequestAuthenticationFilter.class);
#Autowired
private ExceptionMappingAuthenticationFailureHandler authenticationFailureHandler;
#Autowired
private IOpenTokenReader openTokenReader;
private String logoutURL;
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
super.doFilter(httpServletRequest, httpServletResponse, chain);
HttpSession session = httpServletRequest.getSession(false);
if(session != null && session.getAttribute("ssoToken") != null)
{
SsoToken ssoToken = (SsoToken)session.getAttribute("ssoToken");
httpServletResponse.addHeader("agentName", ssoToken.getName());
httpServletResponse.addHeader("agentID", ""+ssoToken.getLoginId());
}
}
/**
*
*/
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
String principal = null;
HttpSession session = null;
try {
session = request.getSession(false);
String tokenName = openTokenReader.getTokenName();
if (tokenName != null && request.getParameter(tokenName.trim()) != null && !request.getParameter(tokenName.trim()).isEmpty()) {
logger.info("Token found in request. Token Name: "+ tokenName);
SsoToken ssoToken = null;
if (session != null) {
session.invalidate();
logger.info("Invalidated old session and creating a new session since request found with new token.");
}
session = request.getSession(true);//create new session
logger.info("New session created: "+session.getId());
Agent agent = openTokenReader.getAgent();
Map result = agent.readToken(request);
if (result != null) {
principal = (String) result.get("subject");
ssoToken = new SsoToken();
ssoToken.setLogoutURL(getLogoutURL());
// ssoToken.setName((String) result.get("lastName") +", "+(String) result.get("firstName"));
ssoToken.setName((String) result.get("firstName"));
ssoToken.setAffiliate((result.get("isAffiliate") != null && !((String)result.get("isAffiliate")).trim().equals("false")) ? true : false);
if(ssoToken.isAffiliate())
ssoToken.setLoginId((String) result.get("affiliateId"));
else
ssoToken.setLoginId((String) result.get("subject"));
}
session.setAttribute("ssoToken", ssoToken);
boolean isInValidToken = hasInvalidTokenData(ssoToken);
if (isInValidToken) {
throw new PreAuthenticatedCredentialsNotFoundException("Invalid Token found in request.");
} else {
session.setAttribute("hasValidToken", true);
}
}
} catch (Exception e) {
logger.error("Exception while reading token " + e);
}
if(session == null )
throw new PreAuthenticatedCredentialsNotFoundException("No session found.");
if(session != null && session.getAttribute("hasValidToken") == null)
throw new PreAuthenticatedCredentialsNotFoundException("No attribute 'hasValidToken' found in session.");
if(session != null && session.getAttribute("hasValidToken") != null && !(Boolean) session.getAttribute("hasValidToken"))
throw new PreAuthenticatedCredentialsNotFoundException("value of attribute 'hasValidToken' is false in session.");
/*if (session == null || session.getAttribute("hasValidToken") == null || !((Boolean) session.getAttribute("hasValidToken"))){
throw new PreAuthenticatedCredentialsNotFoundException("Token not found in request.");
}*/
if(session != null && session.getAttribute("ssoToken") != null)
{
SsoToken ssoToken = (SsoToken)session.getAttribute("ssoToken");
principal = ssoToken.getLoginId();
}
return principal;
}
public boolean hasInvalidTokenData(SsoToken token) {
boolean hasInvalidTokenData = false;
if (token == null) {
hasInvalidTokenData = true;
} else {
if (StringUtils.isBlank(token.getLoginId())) {
logger.debug("Login ID was blank.");
hasInvalidTokenData = true;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Exiting: hasInvalidTokenData(SsoToken)");
logger.debug("hasInvalidTokenData=|" + hasInvalidTokenData + "|");
}
return hasInvalidTokenData;
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "";
}
/**
* #return the logoutURL
*/
public String getLogoutURL() {
return logoutURL;
}
/**
* #param logoutURL the logoutURL to set
*/
public void setLogoutURL(String logoutURL) {
this.logoutURL = logoutURL;
}
public void setAuthenticationFailureHandler(ExceptionMappingAuthenticationFailureHandler authenticationFailureHandler){
this.authenticationFailureHandler = authenticationFailureHandler;
}
public ExceptionMappingAuthenticationFailureHandler getAuthenticationFailureHandler(){
return authenticationFailureHandler;
}
The below code Filters the unauthorized/Session expired requests for a particular endpoint. The Bean should be configured along with the endpoint URL so the Auth filter will be applicable for that endpoint. Now by doing so you can restric the filter application for only the endpoints which you want.
#Bean
public FilterRegistrationBean<AuthFilter> filterRegistrationBean() {
FilterRegistrationBean<AuthFilter> registrationBean = new FilterRegistrationBean<>();
AuthFilter authFilter = new AuthFilter();
registrationBean.setFilter(authFilter);
// Include the URL patterns for which the Auth filter should be applicable
registrationBean.addUrlPatterns("/api/protectedendpoint/*");
return registrationBean;
}
Auth Filter
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class AuthFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String authHeader = httpRequest.getHeader("Authorization");
if (authHeader != null) {
String[] authHeaderArr = authHeader.split("Bearer ");
if (authHeaderArr.length > 1 && authHeaderArr[1] != null) {
String token = authHeaderArr[1];
try {
Claims claims = Jwts.parser().setSigningKey(YOUR_JWT_SECRET_KEY).parseClaimsJws(token).getBody();
httpRequest.setAttribute("email", claims.get("email").toString());
} catch (Exception e) {
httpResponse.sendError(HttpStatus.FORBIDDEN.value(), "invalid/expired token");
return;
}
} else {
httpResponse.sendError(HttpStatus.FORBIDDEN.value(), "Authorization token must be Bearer [token]");
return;
}
} else {
httpResponse.sendError(HttpStatus.UNAUTHORIZED.value(), "Authorization token must be provided");
return;
}
chain.doFilter(httpRequest, httpResponse);
}
}```
I try to use KSOAP2 with Basic Authentication. I´m download ksoap2-android-assembly-3.0.0-jar-with-dependencies.jar, ksoap2-extras-3.0.0.jar, ksoap2-extra-ntlm-3.0.0.jar. I Tried to use the code below:
ArrayList<HeaderProperty> headerProperty = new ArrayList<HeaderProperty>();
headerProperty.add(new HeaderProperty("Authorization", "Basic " +
org.kobjects.base64.Base64.encode("user:password".getBytes())));
it's generate the error:
java.io.IOException: HTTP request failed, HTTP status: 401
ERROR:java.io.IOException:HTTP request failed, HTTP status: 401
I Tried to use the code below too:
HttpTransportBasicAuth androidHttpTpAut = new HttpTransportBasicAuth(URL, "user", "password");
androidHttpTpAut.getServiceConnection().connect();
again not work, generate the error:
Exception in thread "main" java.lang.NoClassDefFoundError: org/ksoap2/transport/HttpTransport
at java.lang.ClassLoader.defineClass1(Native Method)
Anybody do this work fine ?
After many test, I found that the code below need to change:
ArrayList headerProperty = new ArrayList();
headerProperty.add(new HeaderProperty("Authorization", "Basic " +
org.kobjects.base64.Base64.encode("user:password".getBytes())));
androidHttpTransport.call(SOAP_ACTION, envelope);
Change above androidHttpTransport.call() to below polymorphic method:
androidHttpTransport.call(SOAP_ACTION, envelope, headerProperty);
It´s necessary put the parameter headerProperty in method androidHttpTransport.call.
And, I use only ksoap2-android-assembly-3.0.0-jar-with-dependencies.jar in project.
Thanks
You can try the following code. It worked in my case, hope it will help you too.
Create a class called HttpTransportBasicAuth
import org.ksoap2.transport.*;
import org.ksoap2.transport.HttpTransportSE;
import java.io.*;
public class HttpTransportBasicAuth extends HttpTransportSE {
private String username;
private String password;
public HttpTransportBasicAuth(String url, String username, String password) {
super(url);
this.username = username;
this.password = password;
}
public ServiceConnection getServiceConnection() throws IOException {
ServiceConnectionSE midpConnection = new ServiceConnectionSE(url);
addBasicAuthentication(midpConnection);
return midpConnection;
}
protected void addBasicAuthentication(ServiceConnection midpConnection) throws IOException {
if (username != null && password != null) {
StringBuffer buf = new StringBuffer(username);
buf.append(':').append(password);
byte[] raw = buf.toString().getBytes();
buf.setLength(0);
buf.append("Basic ");
org.kobjects.base64.Base64.encode(raw, 0, raw.length, buf);
midpConnection.setRequestProperty("Authorization", buf.toString());
}
}
}
And then, change the following in your proxy class (refer the proxy class below)
protected org.ksoap2.transport.Transport createTransport()
{
return new HttpTransportBasicAuth(url,username,password);
}
Proxy class
import java.util.List;
import org.ksoap2.HeaderProperty;
import org.ksoap2.SoapFault;
import org.ksoap2.serialization.AttributeContainer;
import org.ksoap2.serialization.PropertyInfo;
import org.ksoap2.serialization.SoapObject;
public class Z_WS_SCAN_REPLENISHMENT
{
interface IWcfMethod
{
ExtendedSoapSerializationEnvelope CreateSoapEnvelope() throws java.lang.Exception;
Object ProcessResult(ExtendedSoapSerializationEnvelope envelope,SoapObject result) throws java.lang.Exception;
}
String url="http://example.com/z_ws_scan_replenishment/200/z_ws_scan_replenishment/z_ws_scan_replenishment";
String username = "username";
String password = "password";
int timeOut=60000;
public List< HeaderProperty> httpHeaders;
IServiceEvents callback;
public Z_WS_SCAN_REPLENISHMENT(){}
public Z_WS_SCAN_REPLENISHMENT (IServiceEvents callback)
{
this.callback = callback;
}
public Z_WS_SCAN_REPLENISHMENT(IServiceEvents callback,String url)
{
this.callback = callback;
this.url = url;
}
public Z_WS_SCAN_REPLENISHMENT(IServiceEvents callback,String url,int timeOut)
{
this.callback = callback;
this.url = url;
this.timeOut=timeOut;
}
protected org.ksoap2.transport.Transport createTransport()
{
return new HttpTransportBasicAuth(url,username,password);
}
protected ExtendedSoapSerializationEnvelope createEnvelope()
{
return new ExtendedSoapSerializationEnvelope();
}
protected void sendRequest(String methodName,ExtendedSoapSerializationEnvelope envelope,org.ksoap2.transport.Transport transport) throws java.lang.Exception
{
transport.call(methodName, envelope,httpHeaders);
}
Object getResult(Class destObj,SoapObject source,String resultName,ExtendedSoapSerializationEnvelope __envelope) throws java.lang.Exception
{
if (source.hasProperty(resultName))
{
Object j=source.getProperty(resultName);
if(j==null)
{
return null;
}
Object instance=__envelope.get((AttributeContainer)j,destObj);
return instance;
}
else if( source.getName().equals(resultName)) {
Object instance=__envelope.get(source,destObj);
return instance;
}
return null;
}
public Bapireturn1 ZScanReplenishment(final String ILgnum,final String IPernr,final String IScannedId,final String IScannedLgpl ) throws java.lang.Exception
{
return (Bapireturn1)execute(new IWcfMethod()
{
#Override
public ExtendedSoapSerializationEnvelope CreateSoapEnvelope(){
ExtendedSoapSerializationEnvelope __envelope = createEnvelope();
SoapObject __soapReq = new SoapObject("urn:sap-com:document:sap:soap:functions:mc-style", "ZScanReplenishment");
__envelope.setOutputSoapObject(__soapReq);
PropertyInfo __info=null;
__info = new PropertyInfo();
__info.namespace="";
__info.name="ILgnum";
__info.type=PropertyInfo.STRING_CLASS;
__info.setValue(ILgnum);
__soapReq.addProperty(__info);
__info = new PropertyInfo();
__info.namespace="";
__info.name="IPernr";
__info.type=PropertyInfo.STRING_CLASS;
__info.setValue(IPernr);
__soapReq.addProperty(__info);
__info = new PropertyInfo();
__info.namespace="";
__info.name="IScannedId";
__info.type=PropertyInfo.STRING_CLASS;
__info.setValue(IScannedId);
__soapReq.addProperty(__info);
__info = new PropertyInfo();
__info.namespace="";
__info.name="IScannedLgpl";
__info.type=PropertyInfo.STRING_CLASS;
__info.setValue(IScannedLgpl);
__soapReq.addProperty(__info);
return __envelope;
}
#Override
public Object ProcessResult(ExtendedSoapSerializationEnvelope __envelope,SoapObject __result)throws java.lang.Exception {
return (Bapireturn1)getResult(Bapireturn1.class,__result,"EReturn",__envelope);
}
},"");
}
protected Object execute(IWcfMethod wcfMethod,String methodName) throws java.lang.Exception
{
org.ksoap2.transport.Transport __httpTransport=createTransport();
ExtendedSoapSerializationEnvelope __envelope=wcfMethod.CreateSoapEnvelope();
sendRequest(methodName, __envelope, __httpTransport);
Object __retObj = __envelope.bodyIn;
if (__retObj instanceof SoapFault){
SoapFault __fault = (SoapFault)__retObj;
throw convertToException(__fault,__envelope);
}else{
SoapObject __result=(SoapObject)__retObj;
return wcfMethod.ProcessResult(__envelope,__result);
}
}
java.lang.Exception convertToException(SoapFault fault,ExtendedSoapSerializationEnvelope envelope)
{
return new java.lang.Exception(fault.faultstring);
}
}
These changes did the magic for me. Hope it will work for you as well.
I have tried all the things to connect Facebook with XMPP but i have faced only
one error all the time which is :
SASL authentication failed using mechanism DIGEST-MD5 I am implementing following method to perform this task :
public class MySASLDigestMD5Mechanism extends SASLMechanism {
public MySASLDigestMD5Mechanism(SASLAuthentication saslAuthentication) {
super(saslAuthentication);
}
protected void authenticate() throws IOException, XMPPException {
String[] mechanisms = { getName() };
Map<String, String> props = new HashMap<String, String>();
sc = Sasl.createSaslClient(mechanisms, null, "xmpp", hostname, props, this);
super.authenticate();
}
public void authenticate(String username, String host, String password) throws IOException, XMPPException {
this.authenticationId = username;
this.password = password;
this.hostname = host;
String[] mechanisms = { getName() };
Map<String,String> props = new HashMap<String,String>();
sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, this);
super.authenticate();
}
public void authenticate(String username, String host, CallbackHandler cbh) throws IOException, XMPPException {
String[] mechanisms = { getName() };
Map<String,String> props = new HashMap<String,String>();
sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, (org.apache.harmony.javax.security.auth.callback.CallbackHandler) cbh);
super.authenticate();
}
protected String getName() {
return "DIGEST-MD5";
}
/*public void challengeReceived1(String challenge) throws IOException {
// Build the challenge response stanza encoding the response text
StringBuilder stanza = new StringBuilder();
byte response[];
if (challenge != null) {
response = sc.evaluateChallenge(Base64.decode(challenge));
} else {
response = sc.evaluateChallenge(null);
}
String authenticationText="";
if (response != null) { // fix from 3.1.1
authenticationText = Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
if (authenticationText.equals("")) {
authenticationText = "=";
}
}
stanza.append("<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
stanza.append(authenticationText);
stanza.append("</response>");
// Send the authentication to the server
getSASLAuthentication().send(stanza.toString());
}*/
public void challengeReceived(String challenge)
throws IOException {
byte response[];
if (challenge != null) {
response = sc.evaluateChallenge(Base64.decode(challenge));
} else {
response = sc.evaluateChallenge(new byte[0]);
}
Packet responseStanza;
if (response == null) {
responseStanza = new Response();
} else {
responseStanza = new Response(Base64.encodeBytes(response, Base64.DONT_BREAK_LINES));
}
getSASLAuthentication().send(responseStanza);
}
}
And Connection Function is :
try{
SASLAuthentication.registerSASLMechanism("DIGEST-MD5",MySASLDigestMD5Mechanism. class);
ConnectionConfiguration config = new ConnectionConfiguration("chat.facebook.com",5222);
config.setSASLAuthenticationEnabled(true);
config.setRosterLoadedAtLogin (true);
connection = new XMPPConnection(config);
connection.connect();
Log.d("Connect...", "Afetr Connect");
connection.login("username#chat.facebook.com", "password");
Log.d("done","XMPP client logged in");
}
catch(XMPPException ex)
{
Log.d("not done","in catchhhhhhhhh");
System.out.println(ex.getMessage ());
connection.disconnect();
}
}
but "After connect" it gone to the ctach and give me error like :
SASL authentication failed using mechanism DIGEST-MD5
I searched all blog and find same thing but i dont know what am i doing wrong here..
If is there any other way or solution to connect Facebook XMPP then please Help me
ASAP
Finally, thanks to the no.good.at.coding code and the suggestion of harism, I've been able to connect to the Facebook chat. This code is the Mechanism for the Asmack library (the Smack port for Android). For the Smack library is necessary to use the no.good.at.coding mechanism.
SASLXFacebookPlatformMechanism.java:
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import org.apache.harmony.javax.security.auth.callback.CallbackHandler;
import org.apache.harmony.javax.security.sasl.Sasl;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.util.Base64;
public class SASLXFacebookPlatformMechanism extends SASLMechanism
{
private static final String NAME = "X-FACEBOOK-PLATFORM";
private String apiKey = "";
private String applicationSecret = "";
private String sessionKey = "";
/** * Constructor. */
public SASLXFacebookPlatformMechanism(SASLAuthentication saslAuthentication)
{
super(saslAuthentication);
}
#Override
protected void authenticate() throws IOException, XMPPException
{
getSASLAuthentication().send(new AuthMechanism(NAME, ""));
}
#Override
public void authenticate(String apiKeyAndSessionKey, String host, String applicationSecret) throws IOException, XMPPException
{
if (apiKeyAndSessionKey == null || applicationSecret == null)
{
throw new IllegalArgumentException("Invalid parameters");
}
String[] keyArray = apiKeyAndSessionKey.split("\\|", 2);
if (keyArray.length < 2)
{
throw new IllegalArgumentException( "API key or session key is not present"); }
this.apiKey = keyArray[0];
this.applicationSecret = applicationSecret;
this.sessionKey = keyArray[1];
this.authenticationId = sessionKey;
this.password = applicationSecret;
this.hostname = host;
String[] mechanisms = { "DIGEST-MD5" };
Map<String, String> props = new HashMap<String, String>();
this.sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props,this);
authenticate();
}
#Override
public void authenticate(String username, String host, CallbackHandler cbh)throws IOException, XMPPException
{
String[] mechanisms = { "DIGEST-MD5" };
Map<String, String> props = new HashMap<String, String>();
this.sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props,cbh);
authenticate();
} #Override protected String getName()
{
return NAME;
}
#Override
public void challengeReceived(String challenge) throws IOException
{
byte[] response = null;
if (challenge != null)
{
String decodedChallenge = new String(Base64.decode(challenge));
Map<String, String> parameters = getQueryMap(decodedChallenge);
String version = "1.0";
String nonce = parameters.get("nonce");
String method = parameters.get("method");
long callId = new GregorianCalendar().getTimeInMillis();
String sig = "api_key=" + apiKey + "call_id=" + callId + "method=" + method + "nonce=" + nonce + "session_key=" + sessionKey + "v=" + version + applicationSecret;
try
{
sig = md5(sig);
}
catch (NoSuchAlgorithmException e)
{
throw new IllegalStateException(e);
}
String composedResponse = "api_key=" + URLEncoder.encode(apiKey, "utf-8") + "&call_id=" + callId + "&method="+ URLEncoder.encode(method, "utf-8") + "&nonce="+ URLEncoder.encode(nonce, "utf-8")+ "&session_key="+ URLEncoder.encode(sessionKey, "utf-8") + "&v="+ URLEncoder.encode(version, "utf-8") + "&sig="+ URLEncoder.encode(sig, "utf-8");response = composedResponse.getBytes("utf-8");
}
String authenticationText = "";
if (response != null)
{
authenticationText = Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
}
// Send the authentication to the server
getSASLAuthentication().send(new Response(authenticationText));
}
private Map<String, String> getQueryMap(String query)
{
Map<String, String> map = new HashMap<String, String>();
String[] params = query.split("\\&");
for (String param : params)
{
String[] fields = param.split("=", 2);
map.put(fields[0], (fields.length > 1 ? fields[1] : null));
}
return map;
}
private String md5(String text) throws NoSuchAlgorithmException,UnsupportedEncodingException
{
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(text.getBytes("utf-8"), 0, text.length());
return convertToHex(md.digest());
}
private String convertToHex(byte[] data)
{
StringBuilder buf = new StringBuilder();
int len = data.length;
for (int i = 0; i < len; i++)
{
int halfByte = (data[i] >>> 4) & 0xF;
int twoHalfs = 0;
do
{
if (0 <= halfByte && halfByte <= 9)
{
buf.append((char) ('0' + halfByte));
}
else
{
buf.append((char) ('a' + halfByte - 10));
}
halfByte = data[i] & 0xF;
}
while (twoHalfs++ < 1);
}
return buf.toString();
}
}
To use it:
ConnectionConfiguration config = new ConnectionConfiguration("chat.facebook.com", 5222);
config.setSASLAuthenticationEnabled(true);
XMPPConnection xmpp = new XMPPConnection(config);
try
{
SASLAuthentication.registerSASLMechanism("X-FACEBOOK-PLATFORM", SASLXFacebookPlatformMechanism.class);
SASLAuthentication.supportSASLMechanism("X-FACEBOOK-PLATFORM", 0);
xmpp.connect();
xmpp.login(apiKey + "|" + sessionKey, sessionSecret, "Application");
}
catch (XMPPException e)
{
xmpp.disconnect();
e.printStackTrace();
}
apiKey is the API key given in the application settings page in Facebook. sessionKey is the second part of the access token. If the token is in this form, AAA|BBB|CCC, the BBB is the session key. sessionSecret is obtained using the old REST API with the method auth.promoteSession. To use it, it's needed to make a Http get to this url:
https://api.facebook.com/method/auth.promoteSession?access_token=yourAccessToken
Despite of the Facebook Chat documentation says that it's needed to use your application secret key, only when I used the key that returned that REST method I was able to make it works. To make that method works, you have to disable the Disable Deprecated Auth Methods option in the Advance tab in your application settings.
I solved this problem. I find solution that http://community.igniterealtime.org/thread/41080
Jerry Magill wrote this...
import java.io.IOException;
import java.util.HashMap;
import javax.security.auth.callback.CallbackHandler;
import javax.security.sasl.Sasl;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.util.Base64;
public class MySASLDigestMD5Mechanism extends SASLMechanism
{
public MySASLDigestMD5Mechanism(SASLAuthentication saslAuthentication)
{
super(saslAuthentication);
}
protected void authenticate()
throws IOException, XMPPException
{
String mechanisms[] = {
getName()
};
java.util.Map props = new HashMap();
sc = Sasl.createSaslClient(mechanisms, null, "xmpp", hostname, props, this);
super.authenticate();
}
public void authenticate(String username, String host, String password)
throws IOException, XMPPException
{
authenticationId = username;
this.password = password;
hostname = host;
String mechanisms[] = {
getName()
};
java.util.Map props = new HashMap();
sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, this);
super.authenticate();
}
public void authenticate(String username, String host, CallbackHandler cbh)
throws IOException, XMPPException
{
String mechanisms[] = {
getName()
};
java.util.Map props = new HashMap();
sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, cbh);
super.authenticate();
}
protected String getName()
{
return "DIGEST-MD5";
}
public void challengeReceived(String challenge)
throws IOException
{
//StringBuilder stanza = new StringBuilder();
byte response[];
if(challenge != null)
response = sc.evaluateChallenge(Base64.decode(challenge));
else
//response = sc.evaluateChallenge(null);
response = sc.evaluateChallenge(new byte[0]);
//String authenticationText = "";
Packet responseStanza;
//if(response != null)
//{
//authenticationText = Base64.encodeBytes(response, 8);
//if(authenticationText.equals(""))
//authenticationText = "=";
if (response == null){
responseStanza = new Response();
} else {
responseStanza = new Response(Base64.encodeBytes(response,Base64.DONT_BREAK_LINES));
}
//}
//stanza.append("<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
//stanza.append(authenticationText);
//stanza.append("</response>");
//getSASLAuthentication().send(stanza.toString());
getSASLAuthentication().send(responseStanza);
}
}
It is then called thus from the JabberSmackAPI:
public void login(String userName, String password) throws XMPPException
{
SASLAuthentication.registerSASLMechanism("DIGEST-MD5",MySASLDigestMD5Mechanism. class);
ConnectionConfiguration config = new ConnectionConfiguration("chat.facebook.com",5222);
config.setSASLAuthenticationEnabled(true);
config.setRosterLoadedAtLogin (true);
connection = new XMPPConnection(config);
connection.connect();
connection.login(userName, password);
}