I've seen posts about how to handle smartGWT relogin with SpringSecurity.
Here is my way of doing it.
-create own classes that replaces some of generic spring security filters
so they can send markers that is evaluated by relogin mechanism.
-Wrap entire application with a relogin mechanism
(an implementation of relogin.js)
-intercept requests to IDACall*
Application is called Biztrack and here is how it is done.
Here is my way of doing it.
-create own classes that replaces some of generic spring security filters
so they can send markers that is evaluated by relogin mechanism.
-Wrap entire application with a relogin mechanism
(an implementation of relogin.js)
-intercept requests to IDACall*
Application is called Biztrack and here is how it is done.
Code:
public class Biztrack implements EntryPoint {
private static final String CREDENTIALS_URL="/biztrack/j_spring_security_check";
private LoginWindow loginWindow;
private LoginForm loginForm;
private ApplicationPanel panel;
public void onModuleLoad() {
RPCManager.setLoginRequiredCallback(new LoginRequiredCallback(){
@Override
public void loginRequired(int i, RPCRequest rpcRequest, RPCResponse rpcResponse) {
if(loginWindow==null)
loginWindow=new LoginWindow();
if(!(loginWindow.isVisible() && loginWindow.isDrawn())){
loginForm.clearValues();
loginForm.focusInItem("username");
}
loginWindow.show();
loginWindow.bringToFront();
}
});
panel=new ApplicationPanel();
panel.draw();
}
public class LoginWindow extends Window {
public LoginWindow(){
loginForm=new LoginForm(CREDENTIALS_URL);
setShowModalMask(true);
centerInPage();
setShowCloseButton(false);
setShowMinimizeButton(false);
setIsModal(true);
setAutoSize(true);
addItem(loginForm);
}
}
private class LoginForm extends DynamicForm {
private String credentialsURL;
public LoginForm(String credentialsURL){
this.credentialsURL=credentialsURL;
BlurbItem blurbItem=new BlurbItem("loginFailure");
blurbItem.setVisible(false);
blurbItem.setColSpan(2);
blurbItem.setDefaultValue("Invalid username or password");
blurbItem.setCellStyle("formCellError");
TextItem textItem=new TextItem("username");
textItem.setTitleOrientation(TitleOrientation.LEFT);
textItem.addKeyPressHandler(new KeyPressHandler(){
@Override
public void onKeyPress(KeyPressEvent keyPressEvent) {
if(keyPressEvent.getKeyName().equals("Enter")){
focusInItem("password");
}
}
});
PasswordItem passwordItem=new PasswordItem("password");
passwordItem.setTitleOrientation(TitleOrientation.LEFT);
passwordItem.addKeyPressHandler(new KeyPressHandler(){
@Override
public void onKeyPress(KeyPressEvent keyPressEvent) {
if(keyPressEvent.getKeyName().equals("Enter")){
doLogin();
}
}
});
ButtonItem buttonItem=new ButtonItem("Login");
buttonItem.addClickHandler(new ClickHandler(){
@Override
public void onClick(ClickEvent clickEvent) {
doLogin();
}
});
setFields(textItem,passwordItem,buttonItem);
}
public void doLogin(){
RPCRequest request=new RPCRequest();
request.setContainsCredentials(true);
request.setActionURL(credentialsURL);
request.setUseSimpleHttp(true);
request.setShowPrompt(false);
Map<String,String> params=new HashMap<String,String>();
params.put("j_username",getValueAsString("username"));
params.put("j_password",getValueAsString("password"));
request.setParams(params);
RPCManager.sendRequest(request,new RPCCallback(){
@Override
public void execute(RPCResponse response, Object rawData, RPCRequest request) {
clearValues();
if(response.getStatus()==RPCResponse.STATUS_SUCCESS){
hideItem("loginFailure");
RPCManager.resendTransaction();
loginWindow.hide();
}else if(response.getStatus()==RPCResponse.STATUS_LOGIN_INCORRECT){
showItem("loginFailure");
}else if(response.getStatus()==RPCResponse.STATUS_MAX_LOGIN_ATTEMPTS_EXCEEDED){
SC.warn("Max login attempts exceeded.");
}
focusInItem("username");
}
});
}
}
}
Code:
public class BiztrackAuthenticationEntryPoint extends BiztrackMarkerResponseHandler
implements AuthenticationEntryPoint {
public BiztrackAuthenticationEntryPoint(){
setMarkerSnippet(LOGIN_REQUIRED_MARKER);
}
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
handle(httpServletRequest,httpServletResponse,e);
}
}
Code:
public class BiztrackAuthenticationFailureHandler extends BiztrackMarkerResponseHandler
implements AuthenticationFailureHandler {
protected final Log logger = LogFactory.getLog(getClass());
public BiztrackAuthenticationFailureHandler(){
setMarkerSnippet(LOGIN_REQUIRED_MARKER);
}
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
if(logger.isDebugEnabled())
logger.debug("responded with LOGIN_REQUIRED_MARKER");
handle(httpServletRequest,httpServletResponse,e);
}
}
Code:
public class BiztrackAuthenticationSuccessHandler extends BiztrackMarkerResponseHandler
implements AuthenticationSuccessHandler {
protected final Log logger = LogFactory.getLog(getClass());
public BiztrackAuthenticationSuccessHandler(){
setMarkerSnippet(SUCCESS_MARKER);
}
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
if(logger.isDebugEnabled())
logger.debug("responded with SUCCESS_MARKER");
handle(httpServletRequest,httpServletResponse,authentication);
}
}
Code:
public class BiztrackConcurrentSessionFilter extends GenericFilterBean {
private SessionRegistry sessionRegistry;
private InvalidSessionHandler invalidSessionHandler;
private LogoutHandler[] handlers = new LogoutHandler[] {new SecurityContextLogoutHandler()};
public void afterPropertiesSet(){
Assert.notNull(sessionRegistry,"SessionRegistry required");
Assert.notNull(invalidSessionHandler,"InvalidSessionHandler required");
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {
HttpServletRequest request=(HttpServletRequest)req;
HttpServletResponse response=(HttpServletResponse)res;
HttpSession session=request.getSession(false);
if(session!=null){
SessionInformation info=sessionRegistry.getSessionInformation(session.getId());
if(info!=null){
if(info.isExpired()){
doLogout(request,response);
if(invalidSessionHandler!=null)
invalidSessionHandler.sessionInvalidated(request,response);
else{
response.getWriter().print("This session has been expired (possibly due to multiple concurrent " +
"logins being attempted as the same user).");
response.flushBuffer();
}
return;
}else{
info.refreshLastRequest();
}
}
}
chain.doFilter(request,response);
}
private void doLogout(HttpServletRequest request, HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
for(int i=0; i<handlers.length; i++)
handlers[i].logout(request, response, auth);
}
public void setInvalidSessionHandler(InvalidSessionHandler invalidSessionHandler) {
this.invalidSessionHandler = invalidSessionHandler;
}
public void setLogoutHandlers(LogoutHandler[] handlers) {
Assert.notNull(handlers);
this.handlers = handlers;
}
public void setSessionRegistry(SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
}
}
Code:
public class BiztrackInvalidSessionHandler extends BiztrackMarkerResponseHandler
implements InvalidSessionHandler {
protected final Log logger = LogFactory.getLog(getClass());
public BiztrackInvalidSessionHandler(){
setMarkerSnippet(LOGIN_REQUIRED_MARKER);
}
@Override
public void sessionInvalidated(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
if(logger.isDebugEnabled())
logger.debug("responded with LOGIN_REQUIRED_MARKER");
handle(request,response);
}
}
Code:
public abstract class BiztrackMarkerResponseHandler {
protected static final String LOGIN_REQUIRED_MARKER="<SCRIPT>//'\"]]>>isc_loginRequired\n" +
"//\n" +
"// Embed this whole script block VERBATIM into your login page to enable\n" +
"// SmartClient RPC relogin.\n" +
"\n" +
"while (!window.isc && document.domain.indexOf(\".\") != -1) {\n" +
" try {\n" +
"\t\n" +
" if (parent.isc == null) {\n" +
" document.domain = document.domain.replace(/.*?\\./, '');\n" +
" continue;\n" +
" } \n" +
" break;\n" +
" } catch (e) {\n" +
" document.domain = document.domain.replace(/.*?\\./, '');\n" +
" }\n" +
"}\n" +
"\n" +
"var isc = top.isc ? top.isc : window.opener ? window.opener.isc : null;\n" +
"if (isc) isc.RPCManager.delayCall(\"handleLoginRequired\", [window]);\n" +
"</SCRIPT>";
protected final String SUCCESS_MARKER="<SCRIPT>//'\"]]>>isc_loginSuccess\n" +
"//\n" +
"// When doing relogin with a webserver-based authenticator, protect this page with it and\n" +
"// target your login attempts at this page such that when the login succeeds, this page is\n" +
"// returned.\n" +
"//\n" +
"// If you are integrating with a web service that returns a fault, paste this entire script\n" +
"// block VERBATIM into the fault text.\n" +
"\n" +
"while (!window.isc && document.domain.indexOf(\".\") != -1) {\n" +
" try {\n" +
" if (parent.isc == null) {\n" +
" document.domain = document.domain.replace(/.*?\\./, '');\n" +
" continue;\n" +
" } \n" +
" break;\n" +
" } catch (e) {\n" +
" document.domain = document.domain.replace(/.*?\\./, '');\n" +
" }\n" +
"}\n" +
"\n" +
"var isc = top.isc ? top.isc : window.opener ? window.opener.isc : null;\n" +
"if (isc) isc.RPCManager.delayCall(\"handleLoginSuccess\", [window]);\n" +
"</SCRIPT>";
private String markerSnippet;
protected void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication)
throws IOException, ServletException {
PrintWriter writer=httpServletResponse.getWriter();
writer.print(markerSnippet);
httpServletResponse.flushBuffer();
}
protected void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e)
throws IOException, ServletException {
PrintWriter writer=httpServletResponse.getWriter();
writer.print(markerSnippet);
httpServletResponse.flushBuffer();
}
protected void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws IOException{
PrintWriter writer=httpServletResponse.getWriter();
writer.print(markerSnippet);
httpServletResponse.flushBuffer();
}
public String getMarkerSnippet() {
return markerSnippet;
}
public void setMarkerSnippet(String markerSnippet) {
this.markerSnippet = markerSnippet;
}
}
Code:
public interface InvalidSessionHandler {
void sessionInvalidated(HttpServletRequest request, HttpServletResponse response)throws IOException, ServletException;
}
Code:
spring configuration
<!-- SpringSecurity -->
<security:http entry-point-ref="authenticationEntryPoint">
<security:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter"/>
<security:custom-filter position="FORM_LOGIN_FILTER" ref="formLoginFilter"/>
<security:anonymous enabled="false"/>
<security:intercept-url pattern="/biztrack/sc/IDACall*" access="ROLE_USER"/>
<security:access-denied-handler ref="accessDeniedHandler"/>
<security:session-management session-authentication-strategy-ref="sessionAuthenticationStrategy"/>
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider>
<security:user-service>
<security:user name="test" authorities="ROLE_USER" password="test"/>
<security:user name="guest" authorities="ROLE_USER" password="test"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
<bean id="formLoginFilter"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="sessionAuthenticationStrategy" ref="sessionAuthenticationStrategy"/>
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler">
<bean class="jp.co.alibaba.biztrack.gwt.server.authentication.BiztrackAuthenticationSuccessHandler"/>
</property>
<property name="authenticationFailureHandler">
<bean class="jp.co.alibaba.biztrack.gwt.server.authentication.BiztrackAuthenticationFailureHandler"/>
</property>
</bean>
<bean id="authenticationEntryPoint"
class="jp.co.alibaba.biztrack.gwt.server.authentication.BiztrackAuthenticationEntryPoint"/>
<bean id="accessDeniedHandler"
class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage" value="/accessDenied.htm"/>
</bean>
<bean id="concurrencyFilter" class="jp.co.alibaba.biztrack.gwt.server.authentication.BiztrackConcurrentSessionFilter">
<property name="sessionRegistry" ref="sessionRegistry"/>
<property name="invalidSessionHandler">
<bean class="jp.co.alibaba.biztrack.gwt.server.authentication.BiztrackInvalidSessionHandler"/>
</property>
</bean>
<bean id="sessionAuthenticationStrategy" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<constructor-arg name="sessionRegistry" ref="sessionRegistry"/>
<property name="maximumSessions" value="1"/>
</bean>
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>
</beans>
Code:
web.xml
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Comment