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.
2 comments:
Nice Article, thanks for this information
Also 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?
Post a Comment