SESN0008E when login in ibm/console at the same time with login in application - websphere

Environment: WebSphere AS 9.0.0.10.
I am trying to log in in my app(http://myhost:9080/myapp/login.xhtml), that deployed at WAS and next log in WAS administrative console(https://myhost:9043/ibm/console).
After a successful log in my app in the next browser tab, I try to log in to the administrative console by another user. It returns me 500 HTTP code and in logs:
SESN0008E: A user authenticated as anonymous has attempted to access a session owned by user:defaultWIMFileBasedRealm/CN=dv,OU=Users,OU=S,DC=c,DC=s,DC=u.
SESN0008E: A user authenticated as user:defaultWIMFileBasedRealm/uid=uadm,o=defaultWIMFileBasedRealm has attempted to access a session owned by user:defaultWIMFileBasedRealm/CCN=dv,OU=Users,OU=S,DC=c,DC=s,DC=u.
I guess that is somehow connected with LTPA tokens. As my application and administrative console are in one host, they have a single "LtpaToken2" cookie. When I log in my app I get LTPA for user "dv" and when I log in in ibm/console I get LTPA for user "uadm", that LTPA replaces "LtpaToken2" cookie. If I return the previous "LtpaToken2" cookie by manual, I can open the administrative console again, but in an attempt to log in I get a new LTPA and 500 HTTP code.
I tried to disable "Security integration" on the server, but it didn't give anything.
AuthFilter
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession(true);
WebUser user = (WebUser) session.getAttribute(Constants.webuser);
Principal principal = req.getUserPrincipal();
String jUserLogin = req.getParameter(Constants.jUserLogin);
String jUserPassword = req.getParameter(Constants.jUserPassword);
if (StringUtils.isNotEmpty(jUserLogin)) {
loginBean.setjUserLogin(jUserLogin);
byte[] loginBytes = (jUserLogin + ":" + jUserPassword).getBytes();
String encoded = DatatypeConverter.printBase64Binary(loginBytes);
session.setAttribute(Constants.userLogin, encoded);
}
if (user == null && principal != null) {
user = new WebUser();
loginUser(req, session, user, principal);
}
chain.doFilter(request, response);
}
private void loginUser(HttpServletRequest req, HttpSession session, WebUser user, Principal principal) {
WebUser oldUser = (WebUser) session.getAttribute(Constants.webuser);
user.clean();
user.setLogin(principal.getName());
user.setPass(session.getAttribute(Constants.userLogin));
session.setAttribute(Constants.webuser, user);
}
login.xhtml
<form action="j_security_check" method="POST">
<p:panel styleClass="login-panel block_center" header="LoGIn" style="width:400px;">
<p:panelGrid columns="1" styleClass="no_border_panel_grid" columnClasses="panel_grid_column_250px" style="width: 100%; text-align: center">
<p:inputText id="j_username" placeholder="Login"/>
<p:password id="j_password" placeholder="Pass"/>
<input id="my_button_5" type="submit"
style="width: 77%; margin: 0; padding: 9px; margin-top: 10px" value="Log in"/>
</p:panelGrid>
</p:panel>
</form>
What wrong with it? I have no idea(

Related

Spring Security Maximum session timeout not working

We have this
http.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true)
and
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout-gimli-user")).permitAll()
.deleteCookies("JSESSIONID", "sessionid" /*, "sessdetail", "countfr"*/ );
and
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/login?invalidSession") //dokunma
.maximumSessions(1) //dokunma
.maxSessionsPreventsLogin(true) //dokunma
.expiredUrl("/login?expired")
.sessionRegistry(sessionRegistry());
in
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
And in application.properties, we have
server.servlet.session.timeout=3m
When a user tries to login, user puts password + username, then one time code is sent to user. After this, user needs to put this code and then can view pages.
But if user does not put that code but only puts username+ password, and closes browser, logout is not working. Because logout is not invoked.
But timeout should work and kill after 3 minutes. Or tomcat should kill the session (because we deploy to external tomcat 9).
I tried this
https://stackoverflow.com/a/41450580/11369236
I added
#Bean
public static ServletListenerRegistrationBean httpSessionEventPublisher() {
return new ServletListenerRegistrationBean(new HttpSessionEventPublisher());
}
but still same.
I put
List<SessionInformation> sessions = sessionRegistry.getAllSessions(authentication.getPrincipal(), false);
to
#Override
public Authentication authenticate(Authentication authentication) {
in public class CustomAuthenticationProviderWithRoles implements AuthenticationProvider {
and tried lots of logins without one time code confirm and saw that, sessions are increasing.
But when i try with putting one time code, it does not allow because of maxSessionsPreventsLogin
like here:
https://github.com/spring-projects/spring-security/issues/3078
Login page code:
<form method="POST" th:action="#{/login}">
<input autocomplete="off" class="form-control" id="mobile" name="username"
type="text">
<input autocomplete="off" class="form-control password" name="password"
type="password">
<button class="btn btn btn-block btn-primary btn-lg"
type="submit"
value="Log In">LOGIN
</button>
this is for
login:
http
.formLogin()
.loginPage("/login").permitAll()
and successhandler does this for successfull login:
response.sendRedirect("/otp");
Then, it sets the seconds which to count from for putting code. And sends another view to put code and which contains another form and submit button.
What can be best practice? For example user can close the page after putting user name password but session still remains. Despite there are timeouts.
I can use this and it solves it but I already session timeout in application.properties:
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response
request.getSession(false).setMaxInactiveInterval(11);

Same form in multiple locations in Thymeleaf 3 & Spring 4

I'd like to have my login form (for example) on multiple pages, and I want to pre-populate it with the current user's login (assuming that user is recognized via cookie). But I don't want every controller method for every possible page to have to provide a LoginForm bean for the form. I do want all the validation magic when the form is submitted, and then of course I want the result of the form to the same page the user was on when they submitted it.
I can't quite figure out how to accomplish this right now. Is it even possible?
EDIT:
I've got a Thymeleaf form like this:
<form action="#" data-th-action="#{/users/login}" data-th-object="${loginForm}" method="post">
<input type="text" placeholder="Email or Username" data-th-field="${loginForm.login}">
<input type="password" placeholder="Password" data-th-field="${loginForm.password}">
<button type="submit" name="login">Sign in</button>
<button type="submit" name="register">Register</button>
</form>
If I don’t create a LoginForm (my class) bean and stick it in the model under loginForm, then I get an exception on GET, when rendering the page.
You don't need to pass the LoginForm to multiple controllers, rather, you can centralize your code at a single place with a http filter by validating the Login form for the required urls as below:
LoginFormValidationFilter class:
#Component
public class LoginFormValidationFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String url = request.getRequestURL();
if(url.equals(YOUR_LOGIN_URL)) {
//validate the request here for the required urls
//If request is invalid, send an error message back
}
chain.doFilter(req, res);
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
Web.xml filter mapping:
<filter>
<filter-name>LoginFormValidationFilter </filter-name>
<filter-class>xyz.LoginFormValidationFilter </filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFormValidationFilter </filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

With Spring Security 3.2.0.RELEASE, how can I get the CSRF token in a page that is purely HTML with no tag libs

Today I upgraded from Spring Security 3.1.4 with the separate java config dependency, to the new 3.2.0 release which includes java config. CSRF is on by default and I know I can disable it in my overridden configure method with "http.csrf().disable()". But suppose I don't want to disable it, but I need the CSRF token on my login page where no JSP tag libs or Spring tag libs are being used.
My login page is purely HTML that I use in a Backbone app that I've generated using Yeoman. How would I go about including the CSRF token that's contained in the HttpSession in either the form or as a header so that I don't get the "Expected CSRF token not found. Has your session expired?" exception?
You can obtain the CSRF using the request attribute named _csrf as outlined in the reference. To add the CSRF to an HTML page, you will need to use JavaScript to obtain the token that needs to be included in the requests.
It is safer to return the token as a header than in the body as JSON since JSON in the body could be obtained by external domains. For example your JavaScript could request a URL processed by the following:
CsrfToken token = (CsrfToken) request.getAttribute("_csrf");
// Spring Security will allow the Token to be included in this header name
response.setHeader("X-CSRF-HEADER", token.getHeaderName());
// Spring Security will allow the token to be included in this parameter name
response.setHeader("X-CSRF-PARAM", token.getParameterName());
// this is the value of the token to be included as either a header or an HTTP parameter
response.setHeader("X-CSRF-TOKEN", token.getToken());
Your JavaScript would then obtain the header name or the parameter name and the token from the response header and add it to the login request.
Although #rob-winch is right I would suggest to take token from session. If Spring-Security generates new token in SessionManagementFilter using CsrfAuthenticationStrategy it will set it to Session but not on Request. So it is possible you will end up with wrong csrf token.
public static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = HttpSessionCsrfTokenRepository.class.getName().concat(".CSRF_TOKEN");
CsrfToken sessionToken = (CsrfToken) request.getSession().getAttribute(DEFAULT_CSRF_TOKEN_ATTR_NAME);
Note: I'm using CORS and AngularJS.
Note²: I found Stateless Spring Security Part 1: Stateless CSRF protection which would be interesting to keep the AngularJS' way to handle CSRF.
Instead of using Spring Security CSRF Filter which is based on answers (especially #Rob Winch's one), I used the method described in The Login Page: Angular JS and Spring Security Part II.
In addition to this, I had to add Access-Control-Allow-Headers: ..., X-CSRF-TOKEN (due to CORS).
Actually, I find this method cleaner than adding headers to the response.
Here is the code :
HttpHeaderFilter.java
#Component("httpHeaderFilter")
public class HttpHeaderFilter extends OncePerRequestFilter {
#Autowired
private List<HttpHeaderProvider> providerList;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
providerList.forEach(e -> e.filter(request, response));
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
response.setStatus(HttpStatus.OK.value());
}
else {
filterChain.doFilter(request, response);
}
}
}
HttpHeaderProvider.java
public interface HttpHeaderProvider {
void filter(HttpServletRequest request, HttpServletResponse response);
}
CsrfHttpHeaderProvider.java
#Component
public class CsrfHttpHeaderProvider implements HttpHeaderProvider {
#Override
public void filter(HttpServletRequest request, HttpServletResponse response) {
response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "X-CSRF-TOKEN");
}
}
CsrfTokenFilter.java
#Component("csrfTokenFilter")
public class CsrfTokenFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
CsrfToken csrf = (CsrfToken)request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
}
web.xml
...
<filter>
<filter-name>httpHeaderFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>httpHeaderFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
security-context.xml
...
<custom-filter ref="csrfTokenFilter" after="CSRF_FILTER"/>
...
app.js
...
.run(['$http', '$cookies', function ($http, $cookies) {
$http.defaults.transformResponse.unshift(function (data, headers) {
var csrfToken = $cookies['XSRF-TOKEN'];
if (!!csrfToken) {
$http.defaults.headers.common['X-CSRF-TOKEN'] = csrfToken;
}
return data;
});
}]);
I use thymeleaf with Spring boot. I had the same problem. I diagnosed problem viewing source of returned html via browser. It should be look like this:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example </title>
</head>
<body>
<form method="post" action="/login">
<div><label> User Name : <input type="text" name="username" /> </label></div>
<div><label> Password: <input type="password" name="password" /> </label></div>
<input type="hidden" name="_csrf" value=<!--"aaef0ba0-1c75-4434-b6cf-62c975dcc8ba"--> />
<div><input type="submit" value="Sign In" /></div>
</form>
</body>
</html>
If you can't see this html code. You may be forgot to put th: tag before name and value. <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example </title>
</head>
<body>
<div th:if="${param.error}"> Invalid username and password. </div>
<div th:if="${param.logout}"> You have been logged out. </div>
<form th:action="#{/login}" method="post">
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>

Spring Redirect Url: SavedRequest is null when session expires

I'm accessing the SavedRequest in the login controller to set the redirect url in the login form. When the session expires (or first login after server is started), savedRequest is null.
If I access a url in a fresh browser session, saved request is available.
Any way i can access the redirect url consistently from the login controller?
NB. I'm using spring 3.2.3..from what I can tell savedRequest is no longer accessbile from the session.
login.jsp
<form action="${actionUrl}" method='POST' autocomplete='off'>
<span class="box-label">Username:</span>
<input type="text" name="j_username" data-dojo-type="dijit/form/TextBox" data-dojo-props="style:{width:'150px'}">
<span class="box-label">Password:</span>
<input type="password" name="j_password" data-dojo-type="dijit/form/TextBox" data-dojo-props="style:{width:'150px'}">
<input type="submit" data-dojo-type="dijit/form/Button" data-dojo-props="label:'Login'"/>
<input type="hidden" name="redirect" value="${redirect}">
</form>
Login Controller:
#Controller
public class LoginController {
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String login(final HttpSession session, final Model model, final HttpServletRequest request,
final HttpServletResponse response) {
addRedirectUrlToModel(model, request, response);
return "login";
}
private void addRedirectUrlToModel(final Model model, final HttpServletRequest request, final HttpServletResponse response) {
SavedRequest savedRequest = new HttpSessionRequestCache().getRequest(request, response);
if (savedRequest != null) {
model.addAttribute("redirect", savedRequest.getRedirectUrl());
} else {
System.out.println("saved request is null");
}
}
Security config:
<sec:http auto-config="true" use-expressions="true">
<sec:form-login login-page="/login"
authentication-success-handler-ref="authSuccHandler"
authentication-failure-handler-ref="authFailHandler" />
<sec:logout delete-cookies="JSESSIONID" invalidate-session="true" />
<sec:access-denied-handler error-page="/error/not-authorised"/>
<sec:session-management session-fixation-protection="none"
invalid-session-url="/login/sessionExpired"
session-authentication-error-url="/login/alreadyLoggedIn"
>
<sec:concurrency-control max-sessions="1"
expired-url="/login/sessionExpiredDuplicateLogin"
error-if-maximum-exceeded="false"/>
</sec:session-management>
When you try to get the savedrequest of the expire session, it will come null. You can fix this by creating a new DefaultSavedRequest. After creating the DefaultSavedRequest, you can set into the HttpServletRequest.getSession() and move it during the session in different requests.
private void addRedirectUrlToModel(final Model model, final HttpServletRequest request, final HttpServletResponse response) {
SavedRequest savedRequest = new DefaultSavedRequest(request, new PortResolverImpl());
request.getSession().setAttribute("SPRING_SECURITY_SAVED_REQUEST", savedRequest);
if (savedRequest != null) {
model.addAttribute("redirect", savedRequest.getRedirectUrl());
} else {
System.out.println("saved request is null");
}
}

Display error messages in Spring login

I am using Spring security for authenticating users. I created a custom authentication provider and now I am wondering how I can get error messages from the provider into my form. This is the authenticate() method in my custom authentication provider:
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UserProfile profile = userProfileService.findByEmail(authentication.getPrincipal().toString());
if(profile == null){
throw new UsernameNotFoundException(String.format("Invalid credentials", authentication.getPrincipal()));
}
String suppliedPasswordHash = DigestUtils.shaHex(authentication.getCredentials().toString());
if(!profile.getPasswordHash().equals(suppliedPasswordHash)){
throw new BadCredentialsException("Invalid credentials");
}
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(profile, null, profile.getAuthorities());
return token;
}
This is my form:
<form name='f' action="<c:url value='j_spring_security_check' />" method='POST'>
<div id="data-entry-form">
<div class="form-entry">
<label><spring:message code="login.form.label.email"/></label>
<input type='text' name='j_username' value=''>
</div>
<div class="form-entry">
<label><spring:message code="login.form.label.password"/></label>
<input type='password' name='j_password'/>
</div>
<div class="form-entry">
<input type="submit" value="Verzenden"/>
</div>
</div>
How would I get error messages into my form? From the moment I press the login button, Spring takes over, so the only method I could generate error messages in would be the authenticate() method...
3 Steps of the safest way (we don't rely on the LAST_EXCEPTION):
Specify error page (for example "login-error") in configuration for your custom authentication provider
httpSecurity
.authorizeRequests()
.antMatchers("/css/**", "/js/**", "/img/**").permitAll()
.anyRequest().fullyAuthenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.failureUrl("/login-error")
.and()
.logout().permitAll()
Create controller for url /login-error that returns view of your custom login page (for example "login") with the next code:
#Controller
public class LoginController {
#GetMapping("/login-error")
public String login(HttpServletRequest request, Model model) {
HttpSession session = request.getSession(false);
String errorMessage = null;
if (session != null) {
AuthenticationException ex = (AuthenticationException) session
.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
if (ex != null) {
errorMessage = ex.getMessage();
}
}
model.addAttribute("errorMessage", errorMessage);
return "login";
}
}
Get the error message into your page finally (ThymeLeaf tags for example):
<!--/*#thymesVar id="errorMessage" type="java.lang.String"*/-->
<div class="alert" th:if="${errorMessage}" th:text="${errorMessage}"></div>
I was able to solve it like this:
<c:if test="${param.auth eq 'failure'}">
<div class="error">
<c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />
</div>
</c:if>
Note that you need to detect whether there was an error via a special parameter which you can set in your spring-security config like this:
<security:form-login [...] authentication-failure-url="/login?auth=failure" />
EDIT:
Actually, passing that parameter is not necessary. Instead, one can simply check whether SPRING_SECURITY_LAST_EXCEPTION.message is defined, like this:
<c:if test="${not empty SPRING_SECURITY_LAST_EXCEPTION.message}">
<div class="error">
<c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />
</div>
</c:if>
I think that you should be able to get the messages in the same way than using the "standard" authenticators.
If an exception (or more than one) is thrown in the authentication process, the last exception is stored in a session attribute: SPRING_SECURITY_LAST_EXCEPTION.
So, to get the last exception message from the JSP you can use something like this:
<%=((Exception) request.getSession().getAttribute("SPRING_SECURITY_LAST_EXCEPTION")).getMessage()%>;
Of course, if you have a controller you should probably get the message from the controller and pass only the string to the jsp. This is only an example ;)

Resources