When we create ADF project and enable security, system creates a login page for us. Which has following code.
<form method="POST" action="j_security_check">
<table cellspacing="2" cellpadding="3" border="1">
<tr> <th>Username:</th>
<td> <input type="text" name="j_username"/> </td>
</tr>
<tr> <th>Password:</th>
<td> <input type="password" name="j_password"/> </td>
</tr>
</table>
<p>
<input type="submit" name="submit" value="Submit"/>
</p>
</form>
When user clicks on submit button, login form get submitted. It calls j_security_check, which is provided by framework. It validates username/password against ldap configured in weblogic.
We do not get any point to know what was username/password. At times we may want to derive username/password combination. A usecase could be integration with third party. Imagine you want to integrate with third party system which takes username/password. For example sharepoint webservices. I tried checking OPSS apis but seems like there is no way to retrieve password using them. OPSS apis not retrieving password makes sense also.
To solve our problem I can think of a solution to capture user password while he is trying to login and if authentication is fine store user password in sessionscope. Use it later when calling third party system.
I personally think there might be better ways to do it but I could not find any solution. In this blog I am just describing a way to authenticate user programmatically instead of j_security_check.
Here are the steps:
1. Change login.html to login.jspx: Instead of using normal login.html page lets create a jspx page for login.
Put following form entry in it.
<af:panelFormLayout id="pfl2" styleClass="form_layout">
<f:facet name="footer"/>
<af:inputText label="#{portalBundle.USER_NAME}:" id="it1"
value="#{viewScope.username}"
required="true" maximumLength="50"
partialTriggers="cb1"/>
<af:inputText label="#{portalBundle.PASSWORD}:" id="it2"
value="#{viewScope.password}"
secret="true" required="true"
maximumLength="50" partialTriggers="cb1"
shortDesc="Enter Password"/>
<af:commandButton id="cb1"
action="#{backingBeanScope.LoginPageBean.doLogin}"
text="#{portalBundle.LOGIN}"
styleClass="login"
partialSubmit="true"/>
</af:panelFormLayout>
In above code commandButton-cb1 does a partial submit and calls doLogin method of LoginPageBean. Username and password are stored in viewscope.
LoginPageBean code should look like
import com.kbace.hrc.common.util.diagnostic.DiagnosticUtil;
import java.io.IOException;
import java.util.Map;
import javax.faces.application.FacesMessage;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.security.auth.Subject;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import oracle.adf.share.ADFContext;
import oracle.adf.share.logging.ADFLogger;
import oracle.adf.share.security.SecurityContext;
import oracle.adf.view.rich.component.rich.RichPopup;
import oracle.adf.view.rich.component.rich.output.RichOutputText;
import weblogic.security.URLCallbackHandler;
import weblogic.security.services.Authentication;
import weblogic.servlet.security.ServletAuthentication;
public class LoginPageBean {
private static ADFLogger _logger =
ADFLogger.createADFLogger(LoginPageBean.class);
private RichOutputText pageInit;
private RichPopup loginErrorPopup;
private RichPopup userRoleErrorPopup;
public LoginPageBean() {
}
private String _username, _password;
public String doLogin() {
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
_username = (String)ADFUtil.evaluateEL("#{viewScope.username}");
_password = (String)ADFUtil.evaluateEL("#{viewScope.password}");
String un = _username;
byte[] pw = _password.getBytes();
FacesContext ctx = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest)ctx.getExternalContext().getRequest();
String loginStatus = "login_failed";
try {
Subject subject = Authentication.login(new URLCallbackHandler(un, pw));
weblogic.servlet.security.ServletAuthentication.runAs(subject, request);
ServletAuthentication.generateNewSessionID(request);
loginStatus = "success";
String redirecturl = "/adfAuthentication?success_url=/index.html";
Map session = (Map)ADFUtil.evaluateEL("#{sessionScope}");
session.put("userPassword", _password);
session.put("username", _username);
try {
FacesContext.getCurrentInstance().getExternalContext().redirect(FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath() + redirecturl);
} catch (Exception nm) {
nm.printStackTrace();
}
} catch (FailedLoginException fle) {
ADFUtil.getRequestFlowScope().put("pShowAuthError", "Y");
RichPopup.PopupHints pph=new RichPopup.PopupHints();
this.getLoginErrorPopup().show(pph);
ADFUtil.setEL("#{viewScope.password}", null);
} catch (LoginException le) {
FacesMessage msg =
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error Logging In",
"Error Logging In");
ctx.addMessage(null, msg);
}
return null;
}
}
Above method doLogin tries following things
1. Get username/password from viewscope
2. Authenticate username/password using weblogic security apis.
3. If valid username/password then start a new session.
4. Store username/password in sessionscope
5. Redirect to index.html if successful login.
6. Show error popup if authentication fails.
Also we need to add following libraries in our project
I am not very sure if its best way of doing it. I would definitely love to know better ways of doing this but for time being I just know this way.
Disclaimer: Any views or opinions presented in this blog are solely those of the author and do not necessarily represent those of the company.
<form method="POST" action="j_security_check">
<table cellspacing="2" cellpadding="3" border="1">
<tr> <th>Username:</th>
<td> <input type="text" name="j_username"/> </td>
</tr>
<tr> <th>Password:</th>
<td> <input type="password" name="j_password"/> </td>
</tr>
</table>
<p>
<input type="submit" name="submit" value="Submit"/>
</p>
</form>
When user clicks on submit button, login form get submitted. It calls j_security_check, which is provided by framework. It validates username/password against ldap configured in weblogic.
We do not get any point to know what was username/password. At times we may want to derive username/password combination. A usecase could be integration with third party. Imagine you want to integrate with third party system which takes username/password. For example sharepoint webservices. I tried checking OPSS apis but seems like there is no way to retrieve password using them. OPSS apis not retrieving password makes sense also.
To solve our problem I can think of a solution to capture user password while he is trying to login and if authentication is fine store user password in sessionscope. Use it later when calling third party system.
I personally think there might be better ways to do it but I could not find any solution. In this blog I am just describing a way to authenticate user programmatically instead of j_security_check.
Here are the steps:
1. Change login.html to login.jspx: Instead of using normal login.html page lets create a jspx page for login.
Put following form entry in it.
<af:panelFormLayout id="pfl2" styleClass="form_layout">
<f:facet name="footer"/>
<af:inputText label="#{portalBundle.USER_NAME}:" id="it1"
value="#{viewScope.username}"
required="true" maximumLength="50"
partialTriggers="cb1"/>
<af:inputText label="#{portalBundle.PASSWORD}:" id="it2"
value="#{viewScope.password}"
secret="true" required="true"
maximumLength="50" partialTriggers="cb1"
shortDesc="Enter Password"/>
<af:commandButton id="cb1"
action="#{backingBeanScope.LoginPageBean.doLogin}"
text="#{portalBundle.LOGIN}"
styleClass="login"
partialSubmit="true"/>
</af:panelFormLayout>
In above code commandButton-cb1 does a partial submit and calls doLogin method of LoginPageBean. Username and password are stored in viewscope.
LoginPageBean code should look like
import com.kbace.hrc.common.util.diagnostic.DiagnosticUtil;
import java.io.IOException;
import java.util.Map;
import javax.faces.application.FacesMessage;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.security.auth.Subject;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import oracle.adf.share.ADFContext;
import oracle.adf.share.logging.ADFLogger;
import oracle.adf.share.security.SecurityContext;
import oracle.adf.view.rich.component.rich.RichPopup;
import oracle.adf.view.rich.component.rich.output.RichOutputText;
import weblogic.security.URLCallbackHandler;
import weblogic.security.services.Authentication;
import weblogic.servlet.security.ServletAuthentication;
public class LoginPageBean {
private static ADFLogger _logger =
ADFLogger.createADFLogger(LoginPageBean.class);
private RichOutputText pageInit;
private RichPopup loginErrorPopup;
private RichPopup userRoleErrorPopup;
public LoginPageBean() {
}
private String _username, _password;
public String doLogin() {
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
_username = (String)ADFUtil.evaluateEL("#{viewScope.username}");
_password = (String)ADFUtil.evaluateEL("#{viewScope.password}");
String un = _username;
byte[] pw = _password.getBytes();
FacesContext ctx = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest)ctx.getExternalContext().getRequest();
String loginStatus = "login_failed";
try {
Subject subject = Authentication.login(new URLCallbackHandler(un, pw));
weblogic.servlet.security.ServletAuthentication.runAs(subject, request);
ServletAuthentication.generateNewSessionID(request);
loginStatus = "success";
String redirecturl = "/adfAuthentication?success_url=/index.html";
Map session = (Map)ADFUtil.evaluateEL("#{sessionScope}");
session.put("userPassword", _password);
session.put("username", _username);
try {
FacesContext.getCurrentInstance().getExternalContext().redirect(FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath() + redirecturl);
} catch (Exception nm) {
nm.printStackTrace();
}
} catch (FailedLoginException fle) {
ADFUtil.getRequestFlowScope().put("pShowAuthError", "Y");
RichPopup.PopupHints pph=new RichPopup.PopupHints();
this.getLoginErrorPopup().show(pph);
ADFUtil.setEL("#{viewScope.password}", null);
} catch (LoginException le) {
FacesMessage msg =
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error Logging In",
"Error Logging In");
ctx.addMessage(null, msg);
}
return null;
}
}
Above method doLogin tries following things
1. Get username/password from viewscope
2. Authenticate username/password using weblogic security apis.
3. If valid username/password then start a new session.
4. Store username/password in sessionscope
5. Redirect to index.html if successful login.
6. Show error popup if authentication fails.
Also we need to add following libraries in our project
a. com.bea.core.weblogic.security.auth_1.1.0.0.jar:
$MW_HOME\modules
b. com.bea.core.weblogic.security.identity_1.1.2.1.jar:
$MW_HOME\modules
c. ws.api.jar:
$MW_HOME\wlserver_10.3\server\lib
I am not very sure if its best way of doing it. I would definitely love to know better ways of doing this but for time being I just know this way.
Disclaimer: Any views or opinions presented in this blog are solely those of the author and do not necessarily represent those of the company.
Nice Article, thanks for this information
ReplyDeleteAlso check this out
Laptops, Mobiles, Games, Tv, Smartwatches etc Daily Tech News Updates.
HI . can you elaborate how to change the password from the user end?
ReplyDelete