I am trying to do few things in authentication-success-handler and I need to access few values which was part of initial request data being posted to Spring security.
I am posting following information when user trying to do login
j_username
j_password
storeCode
Spring security is able to authenticate user successfully and is calling "authentication-success-handler".
public class WebshopAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler
{
public WebshopAuthenticationSuccessHandler() {
}
#Override
public void onAuthenticationSuccess(final HttpServletRequest request,
final HttpServletResponse response, final Authentication authentication)
throws IOException, ServletException {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
request.getAttribute( "storeCode" );
attr.getRequest().getAttribute( "storeCode" );
}
}
But in all way, I am not able to get value of storeCode and its coming as null.
Not sure what I am doing wrong.
I am assuming that Spring is creating a new instance of Request and response while calling onAuthenticationSuccess, but how can I pass/ retrieve values which passed passed from the login page?
If the data is from an HTTP POST request, you should be using getParameter, not getAttribute. Attributes are server-side state only, not submitted by the client.
Related
I would like to provide detailed information about a failed login attempt.
Thymleaf shall display regarding information as well as prefill the login form with already given values like email or username. I don't want to send these information as url parameters. From my understanding I need a POST request to the login url.
How can I achieve this using an AuthenticationFailureHandler, and is this even doable without implementing the login process completely manually?
At the moment, all I have is this:
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
#Override
public void onAuthenticationFailure(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception) throws IOException {
String email = request.getParameter("email");
String password = request.getParameter("password");
/* How to make the response POST including data for Thymeleaf? */
redirectStrategy.sendRedirect(request, response, "/login");
}
}
Many thanks for any information
Is this possible to manage multiple login page with spring authorization server?
Suppose, we have 2 client and both client want a different login page
client 1 need /login url
client 2 need /login2 url..
I believe the question is how to brand a login page based on the current clientId. We can use any technique available in Spring Security, as it is fully available and not hidden when using Spring Authorization Server.
As you point out, one way to handle this would be to perform a custom redirect when authentication is required. This would be handled in a custom AuthenticationEntryPoint. You can build a delegate with a mapping of clientIds to login urls. Normally, I'd encourage you to try it yourself (learning new things is fun, right!?), but in this case, here's an example:
public class BrandedAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final Map<String, AuthenticationEntryPoint> authenticationEntryPoints;
private final AuthenticationEntryPoint defaultEntryPoint = new LoginUrlAuthenticationEntryPoint("/login");
public BrandedAuthenticationEntryPoint(Map<String, String> loginUrls) {
Map<String, AuthenticationEntryPoint> map = new HashMap<>();
loginUrls.forEach((clientId, loginUrl) ->
map.put(clientId, new LoginUrlAuthenticationEntryPoint(loginUrl)));
this.authenticationEntryPoints = Collections.unmodifiableMap(map);
}
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
AuthenticationEntryPoint delegate = this.defaultEntryPoint;
// Attempt to resolve a specific login url based on clientId
String clientId = request.getParameter("clientId");
if (clientId != null) {
delegate = this.authenticationEntryPoints.getOrDefault(clientId, this.defaultEntryPoint);
}
delegate.commence(request, response, authException);
}
}
SAS and Form Login are two different filter chains in the default sample, so you would apply this in the normal way on both filter chains:
http.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(new BrandedAuthenticationEntryPoint(...))
);
More information on AuthenticationEntryPoint is available in the reference docs.
I know that spring security creates a cookies names SESSION on successful authentication. Is it possible to get hold of that cookie value in AuthenticationSuccessHandler.
I have a following implementation inside which I need that SESSION cookie value. I looked as response headers of HttpServletResponse, but they have XSRF-TOKEN set-cookie headers,
#Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException {
// GET SESSION, COOKIE VALUE HERE
}
}
Can you please help.
The SESSION cookie is created by Spring Session's DefaultCookieSerializer, which is called every time a new Session is created, and not necessarily after successful authentication.
Spring Session's SessionRepositoryFilter wraps the HttpServletRequest in such a way that whenever you obtain an HttpSession from the request at any point in your application, you're actually getting a Spring Session object. However, this cookie is written to the response after your handler has been called, as you can see in SessionRepositoryFilter:
try {
filterChain.doFilter(wrappedRequest, wrappedResponse);
}
finally {
wrappedRequest.commitSession(); //the SESSION cookie is created if necessary
}
So if the session has just been created for this request...
The cookie won't be available in the HttpServletRequest because the cookie hasn't been sent yet (and so the browser couldn't have sent it)
The cookie won't be HttpServletResponse as a "Set-Cookie" header because it will be written after your application has handled the request.
However, you could get the cookie value:
String cookieValue = request.getSession().getId();
Note: The above code will force Spring Session to create a session backed Redis/Jdbc/etc that will be used later to generate the SESSION cookie.
I got it using the getSession().getId() method from request. My example is using the Webflux implementation with Kotlin but apparently works similar in HttpServletRequest implementation see https://javaee.github.io/javaee-spec/javadocs/javax/servlet/http/HttpServletRequest.html#getSession--
class AuthenticationSuccessHandler : ServerAuthenticationSuccessHandler {
private val location = URI.create("https://redirect.page")
private val redirectStrategy: ServerRedirectStrategy = DefaultServerRedirectStrategy()
override fun onAuthenticationSuccess(webFilterExchange: WebFilterExchange?, authentication: Authentication?): Mono<Void> {
val exchange = webFilterExchange!!.exchange
return exchange.session.flatMap {
it.id // 87b5639c-7404-48a1-b9da-3ca47691a962
this.redirectStrategy.sendRedirect(exchange, location)
}
}
}
I have controller method, which annotated with
#RequestMapping(value = "/someting")
#PreAuthorize("hasAnyRole('ROLE_ACTIVE')")
...
When users without it role transit on this mapping I want to make the users without the appropriate role of the redirect to the home page and displays an alert, the fact that access is denied.
To solve this problem I make custom AccessDeniedHandler, which works perfectly, but only for authenticated users
For users without authentication I found AuthenticationEntryPoint
It looks like
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
AuthenticationException e) throws IOException, ServletException {
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(httpServletRequest);
if(flashMap != null) {
Alerts.addWarningAlert(flashMap, "access denied");
}
httpServletResponse.sendRedirect("/");
}
}
My alert can be added only to flash attributes or model of my main page, but flash map in this method always have null value
How I can solve it without redirecting to other controller, which then redirects to main page and add value to model? Or can I add my flash attributes to http servlet response?
It was possible using Session attributes. I added attribute and then take this attribute from Session in alerts handler.
I have a custom field along with "j_username" and "j_password" on my login.jsp, that I need to authenticate the user. I am using a CustomUsernamePasswordAuthenticationFilter to access the custom field as follows.
public class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
String myCustomField= request.getParameter("myCustomField");
request.getSession().setAttribute("CUSTOM_FIELD", myCustomField);
return super.attemptAuthentication(request, response);
}
}
I tried accessing the session in loadByUsername method of UserDetailsService class but I get an error. Here is the code for my custom UserDetailsService.
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {
ServletRequestAttributes attr = (ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
HttpSession session = attr.getRequest().getSession();
User userObject = dbObject.retrieveUser(userName,myCustomParameter)
// code here to retrieve my user from the DB using the userName and myCustomParameter that was retrieved from login.jsp and put in the session. Get the custom parameter from the session here.
if (userObject == null)
throw new UsernameNotFoundException("user not found");
return new AuthenticationUserDetails(userObject);
}
Is there any way where I can access this custom parameter for authentication? Sending it through the session doesn't seem to be working.
Wouldn't the session be created AFTER the authentication takes place. So a new authenticated session might be created after your call to attemptAuthentication
Here's the spring doc on the Abstract class you're implementing
http://static.springsource.org/spring-security/site/docs/3.0.x/apidocs/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html#successfulAuthentication%28javax.servlet.http.HttpServletRequest,%20javax.servlet.http.HttpServletResponse,%20org.springframework.security.core.Authentication%29
You might be losing the session attribute by the time loadByUsername is called.
I ran into the exact problem.
The problem appeared to be that the RequestAttributes was not bound to the current thread. To make it work, I had to explicitly bind it to the current thread.
In CustomUsernamePasswordAuthenticationFilter, after the statement
request.getSession().setAttribute("CUSTOM_FIELD", myCustomField);
Add:
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
This worked for for me.