JSF 2.0 Simple login page - authentication

I need to restrict the access to a part of the application. In order to access that part, user needs to log in. I have a table in my database called User, with usernames and hashed passwords and a login form that consists of two inputs and a submit. However, I don't know which classes/mathids should I use to log in the user (I assume that there is a support for this functionality in jsf). Also, as far as I know, I need to edit my web.xml to support the authentification. Could someone propose a typical solutions and general steps that I need to do in order to get that functionality (links, tutorials of a value greatly appreciated)?
i also wonder how do I limit the access to another page if the person is not logged in so when the user types in the direct link to a page, he will be redirected to a main login page.
Thanks in advance for any help.
Grem.

You could use the HttpServletRequest API introduced in Servlet 3.0:
/**
* Performs authentication via HttpServletRequest API
*/
public String login(String username, String password) throws IOException {
try {
getRequest().login(username, password);
this.user = userDao.find(username);
} catch (ServletException e) {
JsfUtil.addErrorMessage(JsfUtil.getStringResource("loginFailed"));
return null;
}
return "/index?faces-redirect=true";
}
public String logout() throws ServletException {
this.user = null;
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
if (isAuthenticated())
getRequest().logout();
return "logout";
}
public boolean isAuthenticated() {
return getRequest().getUserPrincipal() != null;
}
public static HttpServletRequest getRequest() {
Object request = FacesContext.getCurrentInstance().getExternalContext().getRequest();
return request instanceof HttpServletRequest
? (HttpServletRequest) request : null;
}

You can use j_security_check. All you do is post to it, and it will handle authentication based on the realm you've defined, and the application-specific configuration in your web.xml.
Depending on your app server, there is an additional step of linking the defined role (app-specific) to a group (realm-specific).
Here is a typical configuration:
<servlet>
<servlet-name>Login</servlet-name>
<servlet-class>com.example.Login</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Login</servlet-name>
<url-pattern>/Login</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Error</servlet-name>
<servlet-class>com.example.Error</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Error</servlet-name>
<url-pattern>/Error</url-pattern>
</servlet-mapping>
<login-config>
<auth-method>FORM</auth-method>
<realm-name>example.com</realm-name>
<form-login-config>
<form-login-page>/Login</form-login-page>
<form-error-page>/Error</form-error-page>
</form-login-config>
</login-config>
<security-role>
<role-name>arbitraryRoleName</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>All Pages</web-resource-name>
<url-pattern>/index.xhtml</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>arbitraryRoleName</role-name>
</auth-constraint>
</security-constraint>
Note the security-role. This still needs linked into a group, or whatever you are defining to differentiate users that can use a page from users who can't.

Related

Java EE, JSF programmaticaly login with form-based does not redirect to user's home page on production

I'm developping a project with several different user profiles / roles. Security is realm-based and a form authentications is used. When a user logs in successfuly it must be redirected to his/her home page according tho the user's role.
All those things are working perfectly on my developping environment. I'm using Netbeans IDE with Payara 5 application server runtime.
To put a beta version on production I've used an Ubuntu machine where I've installed Payara 5 full version. After that (server configuration, realm, jdbc connection, ...) I've realized that when a user logs in successfully and it is redirected to his/her home page the login form is presented again and user can't access to his home-page.
Here is an extract of my web.xml file:
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.xhtml</welcome-file>
</welcome-file-list>
<login-config>
<auth-method>FORM</auth-method>
<realm-name>MyRealmName</realm-name>
<form-login-config>
<form-login-page>/authentication/login.xhtml</form-login-page>
<form-error-page>/authentication/error.xhtml</form-error-page>
</form-login-config>
</login-config>
<security-constraint>
<web-resource-collection>
<web-resource-name>Admin portal</web-resource-name>
<url-pattern>/subfolder/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>role_admin</role-name>
</auth-constraint>
</security-constraint>
<session-config>
<session-timeout>
5
</session-timeout>
</session-config>
To map group names with role names I've used this file 'glassfish-web.xml'
<glassfish-web-app>
<context-root>/WebAppDeplo</context-root>
<security-role-mapping>
<role-name>role_admin</role-name>
<group-name>ADMINISTRATOR</group-name>
</security-role-mapping>
</glassfish-web-app>
Finally, here's the code of login() method
private static String HOME = "/subfolder/index.xhtml?faces-redirect=true";
// ...
public void login() throws IOException {
ExternalContext externalContext
= FacesContext.getCurrentInstance().getExternalContext();
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
log.debug("[bean] - login()");
try{
request.login(this.userName, this.password);
// Authentication OK
String url = extractContextPath() + HOME;
log.debug("[bean] - login() - login OK - redirecting to: " + url);
externalContext.redirect(url);
}catch (ServletException e){
// Authentication failed
log.debug("[bean] - login() - Authentication failed!");
String loginErrorMessage = e.getLocalizedMessage();
FacesContext.getCurrentInstance()
.addMessage(null, new FacesMessage(loginErrorMessage));
}
}
private String extractContextPath() {
ExternalContext externalContext
= FacesContext.getCurrentInstance().getExternalContext();
return externalContext.getRequestContextPath();
}
Several thoughts:
a) Using a BASIC authentication on web.xml. It works fine! Unfortunately, programatically login is required because different user roles must be loged and redirectet to role-based home-page.
b) Authentication mechanism works fine because if a wrong login is entered the ServletException is thrown. When a right user is provided the debug message "[bean] - login() - login OK..." is printed in the log file. No other errors seen on log file.
c) I've tried several values of static variable HOME: "/subfolder", "/subforlder/index", ... All this options work fine on developping environment but not on production environment.
d) I've tried Payara 5.201 and Payara 5 5.2021.3 to avoid specific version server bugs.
e) What is upsetting me is the fact that on developing environment this thing works fine! :-(
I appreciate any ideas you can throw on this matter.
I'm auto responding my question because I guess I've already found an important clue to address this issue.
I've realized that when accessing to production deployment a session cookie is not created in browser.
So, I've found a parameter in web.xml that permits to customize session cookie's name.
Typing this parameter authentication (form-based, and redirecting to user home-page) works fine.
Here is the slice of web.xml affected:
<session-config>
<session-timeout>5</session-timeout>
<cookie-config>
<name>MY_JSESSIONID</name>
</cookie-config>
</session-config>

Mutual Authentication with Reactive Netty on restricted urls

I am using spring cloud gateway to front a legacy application so that we can start migrating things behind the scenes. Some of the urls that are hosted by the application are public facing and some are device restricted. We control the devices and they use a browser client to access the restricted urls. We have mutual authentication setup for the device restricted urls on the server using tomcat and security constraints like this in web.xml:
<security-constraint>
<web-resource-collection>
<web-resource-name>Certificate Content</web-resource-name>
<!-- URL for authentication endpoint - this is locked down with the role assigned by tomcat -->
<url-pattern>/rest/secure/url1</url-pattern>
<url-pattern>/rest/secure/url2</url-pattern>
<url-pattern>/rest/secure/url3</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>certificate</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<!-- All other endpoints- force the switch from http to https with transport-guarantee -->
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Context</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>CLIENT-CERT</auth-method>
</login-config>
<security-role>
<role-name>certificate</role-name>
</security-role>
That is coupled with a truststore setup in tomcat's server.xml (I can add it, but I don't think that is relevant to this conversation).
My goal is to implement a similar setup in spring cloud gateway which is using reactive netty under-the-hood and remove the web.xml restrictions from the legacy application. I think I could switch it to using tomcat and probably get the web.xml from above to work, but I'd rather stick to the performance benefits of using reactive netty.
Key Goals:
Only deploy one api gateway for the app. The number of urls that
require mutual auth is very small so I'd rather not include a whole
other container to manage just to support them.
Do not ask for a client cert on the public urls.
Require valid client certs for the restricted urls.
I've setup mutual authentication and can get it to work with need/want/none as expected (truststores setup, etc), but it applies to ALL urls. I've also setup X509 security restrictions and that all seems to work.
I think what I want to setup is tsl renegotiation using the SslHandler after the http request is decrypted (so that I can access the url) based on the path. But I'm having trouble with the details and I've failed at finding any examples that incorporate spring-boot applications using reactive netty to do a tsl renegotiation. Any tips on how to perform a renegotiation of the ssl connection with needClientAuth set to true would be appreciated. I think I need to invalidate the session or something because when I try to do it manually it appears that it is skipping negotiation because the connection is already marked as negotiated in the ssl engine.
This is one of the iterations I've tried (this doesn't restrict on urls, but I plan to add that after I get this working):
#Component
public class NettyWebServerFactoryGatewayCustomizer implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
private static final Logger LOG = LoggerFactory.getLogger(NettyWebServerFactoryGatewayCustomizer.class);
#Override
public void customize(NettyReactiveWebServerFactory serverFactory) {
serverFactory.addServerCustomizers(httpServer -> {
httpServer = httpServer.wiretap(true);
return httpServer.tcpConfiguration(tcpServer -> {
tcpServer = tcpServer.doOnConnection(connection ->
connection.addHandler("request client cert",
new SimpleChannelInboundHandler<HttpRequest>() {
#Override
protected void channelRead0(ChannelHandlerContext ctx, HttpRequest httpRequest) {
LOG.error("HttpRequest: {}", httpRequest);
final ChannelPipeline pipeline = ctx.pipeline();
final SslHandler sslHandler = pipeline.get(SslHandler.class);
final SSLEngine sslEngine = sslHandler.engine();
sslEngine.setNeedClientAuth(true);
sslHandler.renegotiate()
.addListener(future -> ctx.fireChannelRead(httpRequest));
}
}
)
);
return tcpServer;
});
});
}
}
I see it performing the renegotiation in the debugger, but it still seems to be set to client auth none (as set in the application.properties) instead of need as set in the code before renegotiation. I've tried sslEngine.getSession().invalidate(); but that didn't help. I've also tried generating a new ssl handler from the ssl provider but that seemed to really screw things up.
Thank you for any help provided.
Edit: Doing more research it appears that this approach is not appropriate going forward since ssl renegotiation is being dropped entirely in tsl 1.3 (see https://security.stackexchange.com/a/230327). Is there a way to perform the equivalent of SSL verify client post handshake as described here: https://www.openssl.org/docs/manmaster/man3/SSL_verify_client_post_handshake.html ?
Edit2: Looks like this was an issue where TLS1.3 post handshake was not supported by the browser I was testing with. Setting the server to just accept TLS 1.2 seemed to work. Not sure if there is a better way to solve this but this is what I added to my application.properties:
server.ssl.enabled-protocols=TLSv1.2
Here is what I used to get it to work. I'm going to leave out the spring security side of it since that is separate from requesting the certificate from the client.
There are so many ways to configure the child pipeline that is used to process the request. Please let me know if there is a more accepted way to configure it.
Configure the HttpServer by adding to the bootstrap pipeline that is applied when a connection is established with the client:
#Component
public class NettyWebServerFactoryGatewayCustomizer implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
private static final HttpRenegotiateClientCertHandler HTTP_RENEGOTIATE_CLIENT_CERT_HANDLER =
new HttpRenegotiateClientCertHandler(SecurityConfig.X509_PROTECTED_ENDPOINTS);
#Override
public void customize(NettyReactiveWebServerFactory serverFactory) {
serverFactory.addServerCustomizers(NettyWebServerFactoryGatewayCustomizer::addRenegotiateHandlerToHttpServer);
}
private static HttpServer addRenegotiateHandlerToHttpServer(HttpServer httpServer) {
return httpServer.tcpConfiguration(NettyWebServerFactoryGatewayCustomizer::addRenegotiateHandlerToTcpServer);
}
private static TcpServer addRenegotiateHandlerToTcpServer(TcpServer server) {
return server.doOnBind(NettyWebServerFactoryGatewayCustomizer::addRenegotiateHandlerToServerBootstrap);
}
private static void addRenegotiateHandlerToServerBootstrap(ServerBootstrap serverBootstrap) {
BootstrapHandlers.updateConfiguration(
serverBootstrap,
HttpRenegotiateClientCertHandler.NAME,
NettyWebServerFactoryGatewayCustomizer::addRenegotiateHandlerToChannel
);
}
private static void addRenegotiateHandlerToChannel(ConnectionObserver connectionObserver, Channel channel) {
final ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(HttpRenegotiateClientCertHandler.NAME, HTTP_RENEGOTIATE_CLIENT_CERT_HANDLER);
}
}
Child Handler that performs the renegotiation:
#ChannelHandler.Sharable
public class HttpRenegotiateClientCertHandler extends SimpleChannelInboundHandler<HttpRequest> {
public static final String NAME = NettyPipeline.LEFT + "clientRenegotiate";
private static final PathPatternParser DEFAULT_PATTERN_PARSER = new PathPatternParser();
private final Collection<PathPattern> pathPatterns;
public HttpRenegotiateClientCertHandler(String ... antPatterns) {
Assert.notNull(antPatterns, "patterns cannot be null");
Assert.notEmpty(antPatterns, "patterns cannot be empty");
Assert.noNullElements(antPatterns, "patterns cannot have null items");
pathPatterns = Arrays.stream(antPatterns)
.map(DEFAULT_PATTERN_PARSER::parse)
.collect(Collectors.toSet());
}
#Override
protected void channelRead0(ChannelHandlerContext ctx, HttpRequest request) {
if (shouldNotRenegotiate(request)) {
ctx.fireChannelRead(request);
return;
}
final ChannelPipeline pipeline = ctx.pipeline();
final SslHandler sslHandler = pipeline.get(SslHandler.class);
final SSLEngine sslEngine = sslHandler.engine();
sslEngine.setNeedClientAuth(true);
sslHandler.renegotiate()
.addListener(renegotiateFuture -> ctx.fireChannelRead(request));
}
/**
* Determine if the request uri matches the configured uris for this handler.
* #param request to match the path from.
* #return true if any of the path patterns are matched.
*/
private boolean shouldNotRenegotiate(HttpRequest request) {
final String requestUri = request.uri();
final PathContainer path = PathContainer.parsePath(requestUri);
return pathPatterns.stream()
.noneMatch(matcher -> matcher.matches(path));
}
}
And these configurations in application.properties:
# Setup Client Auth Truststore:
server.ssl.trust-store=<path to truststore>
server.ssl.trust-store-password=<truststore password>
server.ssl.trust-store-type=<truststore type>
# Set to none by default so we do not ask for client auth until needed.
server.ssl.client-auth=none
# This is specifically not including TLSv1.3 because there are issues
# with older browsers' implementation of TLSv1.3 that prevent verify
# client post handshake client from working.
server.ssl.enabled-protocols=TLSv1.2
Edit: Updated because handler gateway route code wasn't being invoked properly.

How to use a filter in a Struts1 application?

I have a Struts1 application and am unsuccessfully trying to get a filter to work in order to add headers/etc to the response after the action has completed, but am not able to get it to work.
By the time the struts action is completed and control is returned to my filter, the response is already committed.
web.xml:
<filter>
<filter-name>workflow</filter-name>
<filter-class>webapp.session.WorkflowModifierFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>workflow</filter-name>
<servlet-name>action</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
My filter is the following:
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if( !servletResponse.isCommitted() )
log.debug("Not Committed! Can modify it!");
filterChain.doFilter(servletRequest, servletResponse);
// depending on the response, I'd like to add headers here
if( servletResponse.getStatus() == 200 )
servletResponse.addIntHeader( "x-success", 1 );
if( servletResponse.isCommitted() )
log.debug("ACCCK! Response Already Committed");
}
However, I noticed that my x-success header was never added. A little digging, and I noticed that my response was already returned/committed by the time the control returned to my filter chain.
What is the proper way to do this in Struts1? Does the filter execution not supposed to wrap the entire servlet? Why is my response being committed prior to my filter finishing? Where else can I add headers based on the response (post action processing) in Struts1?
When you call filterChain.doFilter you pass control from your filter to requested page (ie Struts), which is then free to commit the response if it chooses. Since you want to examine the result of the Struts servlet, you should create a response wrapper class (extends HttpServletRequestWrapper) and pass that to filterChain.doFilter rather than the response parameter that your filter is passed.
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
ServletResponse wrapper = new MyHttpServletRequestWrapper(httpResponse);
filter.doChain(servletRequest, wrapper);
The HttpServletRequestWrapper constructor accepts a HttpServletResponse as input and delegates all methods to the wrapped response, but you can override them in your class as necessary. If you want to prevent the response from being committed you'll want to override methods such as flushBuffer, getOutputSteam (the returned output stream can be flushed, committing the response), and getWriter.
But you may not need to do that - try overriding the setStatus method in the wrapper class to check the status code and add the header when it is called:
public class MyHttpServletResponseWrapper extends HttpServletResponseWrapper {
public MyHttpServletResponseWrapper(HttpServletResponse response) {
super(response);
}
#Override
public void setStatus(int sc) {
if(sc == 200) {
addIntHeader("x-success", 1);
}
super.setStatus(sc);
}
}
Although this question was asked in relation to Struts 1, it applies to any web framework; my answer is based on work I did for JSF filters.

Spring Security: forward to original page after login with admin user after access was denied

I use Spring security to authenticate users. If a user requests a secured page, he has to authenticate over a login page. If the user is always authenticated, he will be redirected to the requested page immediatly. Moreover some pages need special access rights, and so I setup an access-denied-page temporarily. So far so good.
The scenario:
The scenario definies, that the user will get a login-form instead of a static access-denied page, so that a different user can authenticate and if authentication is successful the requested page that needs the higher privileges will open.
The actual spring configuration reads:
<security:http auto-config="true" use-expressions="true" disable-url-rewriting="true">
<security:intercept-url pattern="/index.jsp" access="permitAll" />
<security:intercept-url pattern="/loginView" access="permitAll" />
<security:intercept-url pattern="/accessDenied" access="permitAll"/>
<security:intercept-url pattern="/user" access="hasRole('ROLE_USER')" />
<security:intercept-url pattern="/admin" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/**" access="denyAll"/>
<security:form-login login-page="/loginView"
authentication-failure-url="/loginView"
default-target-url="/dirView" />
<security:logout />
<security:access-denied-handler ref="accessDeniedHandler" />
</security:http>
The accessDeniedHandler-Bean:
public class AccessDeniedServletRequestHandler implements AccessDeniedHandler {
/** {#inheritDoc} */
#Override
public void handle(HttpServletRequest req, HttpServletResponse resp,
AccessDeniedException accessDeniedException) throws IOException,
ServletException {
RequestDispatcher d = req.getRequestDispatcher("/loginView");
d.forward(req, resp);
}
}
But that implementation of AccessDeniedHandler only forwards to the loginView. After authentication of an admin the default-success-page is openend and not the original requested page. I also tried to save the original request by calling HttpServletRequest#getAttribute("javax.servlet.forward.servlet_path"), but I don't understand how to force spring security to use that original request instead of the default target url.
Besides I read about org.springframework.security.web.savedrequest.SavedRequest that is used inside spring authentication to remember the original request if an unauthenticated user requests a page. But I don't find a valid way how to use the SavedRequest in the same manner for my access denied scenario.
Thanks in advance for suggestions and solutions.
I think your requirements should be satisfied by using the RequestCache API.
If you modify your http configuration you can use the request-cache namespace element:
<security:http>
...
<security:request-cache ref="requestCache"
</security:http>
<bean id="requestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache" />
You can also inject it into your AccessDeniedHandler. Then all you should need is a simple to saveRequest to setup the cached request which should be restored post-authentication:
public class AccessDeniedServletRequestHandler implements AccessDeniedHandler {
// Inject this into your class.
private RequestCache requestCache;
#Override
public void handle(HttpServletRequest req, HttpServletResponse resp,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
requestCache.saveRequest(req, resp);
RequestDispatcher d = req.getRequestDispatcher("/loginView");
d.forward(req, resp);
}
}
Strictly speaking, you don't actually need to do the bit with the namespace at all, since HttpSessionRequestCache is stateless (it's the internal implementation which is used if you don't override it in the namespace). So you could just create one directly in your AccessDeniedHandler class and it would still work.

spring security stay on single url at login

here is my case.
when first time user land to site, they will pointed to login page. (lest say http://ex.com/) and when they successfully login, they'll see the other page with the same url (http://ex.com/
but, when they open the site on other tab (http://ex.com) they will pointed back to login page.
how to implement this case in my site with spring security ?
its easy to do when deal with conventional servlet. i just need to have 2 method (doGet for showing login page, and doPost for authenticating user and if its valid it will call another view).
here is my configuration :
<security:http auto-config="true">
<security:intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<security:form-login login-page="/login"
login-processing-url="/loginProcess"
default-target-url="/login"
authentication-failure-url="/login?login_error=1" />
<security:logout logout-url="/logout" logout-success-url="/logoutSuccess" />
</security:http>
** Edited (remove unrelated answer)
It appears you need to add a concurrent session management using Spring Security. See the following link: http://static.springsource.org/spring-security/site/docs/3.1.x/reference/session-mgmt.html
You can inject the SessionRegistry and see if the principal is already logged-in. If he is, call the expireNow()
Or you can implement a filter on or before SessionManagementFilter in the FilterChainProxy:
The SessionManagementFilter checks the contents of the SecurityContextRepository against the current contents of the SecurityContextHolder to determine whether a user has been authenticated during the current request, typically by a non-interactive authentication mechanism, such as pre-authentication or remember-me [19]. If the repository contains a security context, the filter does nothing. If it doesn't, and the thread-local SecurityContext contains a (non-anonymous) Authentication object, the filter assumes they have been authenticated by a previous filter in the stack. It will then invoke the configured SessionAuthenticationStrategy.
- http://static.springsource.org/spring-security/site/docs/3.1.x/reference/session-mgmt.html
I think your configuration has a problem
<security:http auto-config="true">
<security:intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<security:form-login login-page="/login"
login-processing-url="/loginProcess"
default-target-url="<home-page-url. ex: /home>"
authentication-failure-url="/login?login_error=1" />
<security:logout logout-url="/logout" logout-success-url="/logoutSuccess" />
</security:http>
The default-target-url should point to the default page to which the application has to redirect after a successful login.
EDITED
After going through the required posted again, I think the approach is to make the controller handling /login request to handle both cases
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;
#Controller
public class AppsController {
#RequestMapping("/login")
public ModelAndView view(HttpServletRequest request,
HttpServletResponse response) {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
User user = authentication != null
&& authentication.getPrincipal() instanceof User ? (User) authentication
.getPrincipal() : null;
return user == null ? getLoginModelAndView() : getHomeModelAndView();
}
private ModelAndView getHomeModelAndView() {
return null;
}
private ModelAndView getLoginModelAndView() {
return null;
}
}
If there is no authenticated user present in the session the controller will return the log-in page, but once the user is logged-in then it will return a different page.
Spring security will cache the logged used to the user session and it can be retrieved using the SecurityContextHolder.