Method determineTargetUrl in Spring Security 4 not being called - java

I have a CustomLoginSucessHandler in my Spring MVC 4 project to manage an action when the user Logs In.
This is working properly. In the same class I have the method determineTargetUrl to redirect the user according to his ROLE.
Here is the code:
#Override
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response){
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
final String userName = authentication.getName();
log.debug("TARGET URL METHOD!");
List<Authority> authorityList = authorityService.getAllAuthoritiesByUserName(userName);
for(Authority authority: authorityList){
switch (authority.getAuthority()){
case "ROLE_ADMIN":
return "processFile";
case "ROLE_USER":
return "userPortal";
case "ROLE_DEMO1":
return "processFile";
case "ROLE_DEMO2":
return "processFile";
}
}
return "403";
}
See that I have a log.debug("TARGET URL METHOD")
This log is never called and of course the page is not being redirected, it's going to the default landing page that is processFile.html.
I am puzzled why the second method is not being called while my onAuthenticationSuccess works perfectly. They are in the same Class.
Here is the code how I create the instance of my CustomLoginSucessHandler:
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private CustomLoginSucessHandler customLoginSucessHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().formLogin().loginPage("/login.html")
.loginProcessingUrl("/login").permitAll().and().logout().logoutSuccessUrl("/")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).permitAll().and().exceptionHandling()
.accessDeniedPage("/403.html");
http.csrf().requireCsrfProtectionMatcher(new CsrfRequestMatcher());
http.formLogin().successHandler(customLoginSucessHandler);
}
Thank you.

You are trying to ovverride the wrong function, that is the root cause of your issue. In the excerpt you provided you have a function that seems to be overriding another:
#Override
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response){
but in fact it is not overriding anything. If you check the javadoc of AuthenticationSuccessHandler, you will see that it provides only one function: onAuthenticationSuccess which you reported as "working". It works, but it is an overriden function and it does get called as part of the standard login procedure. If you follow closely this example:
CustomLoginSuccessHandler example (probably you followed this already)
you will see that the determineTargetUrl function is not overriden, but explicitly called by the implementation:
protected void handle(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException {
String targetUrl = determineTargetUrl(authentication);
which handle method in turn is also being called from:
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException {
handle(request, response, authentication);
clearAuthenticationAttributes(request);
}

Related

Spring #RequestMapping

I have a question with Spring MVC RequestMapping annotation. need your help.
I have created one IPSLcontroller and i want that IPSLcontroller to handle all request url.i have created two method in this controller.
1)handleLogoutRequest :- this method should invoke on below url.
2)handleRequest :- this method should invoke on all request url otherthan logout.
http://localhost:9086/webapp/login
or
http://localhost:9086/webapp/add
or
http://localhost:9086/webapp/remove
here is my sample code. but it's not working as expected.
#Controller
public class IPSLController {
#RequestMapping(value={"/logout/*"},method = RequestMethod.POST)
protected void handleLogoutRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
System.out
.println("........................IPSLController logout request.......................................");
}
#RequestMapping(method = RequestMethod.POST,value={"/*"})
protected void handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
System.out
.println("........................IPSLController all request Post.......................................");
}
}
You should use a general Prefix for every controller you use, so you can differ between them better. Also you donĀ“t need any "/" for calls like this.
#Controller
#RequestMapping("ispl")
public class IPSLController {
#RequestMapping(value={"logout"},method = RequestMethod.POST)
protected void handleLogoutRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
System.out
.println("........................IPSLController logout request.......................................");
}
#RequestMapping(method = RequestMethod.POST,value={"hello"})
protected void handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
System.out
.println("........................IPSLController all request Post.......................................");
}
}
If you now want to call them over a ServletRequest or with a restService or something similar you should declare them like this
#GET
#Path("ispl/logout")
public void Method (HttpServletResponse ...)
Well it is working the way it should. You have a mapping for /* and for /logout/*. So when you post to /logout it invokes the method for /*. I suspect that if you post to /logout/something it would invoke your logout handler.
If you want it to work, you cannot have a wildcard mapping for the second method. At least use /something/* so that spring can make a correct decision on mappings.

Spring Security and Action Required after login

I'm trying to implement an action required screen after user is logged-in in Spring Security? I have a requirement where user has to perform to complete a form (change password, accept Terms Of Use, etc.), then once user completes that action he can use the rest of the app. I'm using Spring OAuth2 with the login screen that uses Spring Security flow.
So far I have tried to use http.formLogin().successHandler() that has custom implementation of SavedRequestAwareAuthenticationSuccessHandler, which detects if user has action required, then redirects user to the page when he can fill out the form, but the problem with that is that if user navigates away from that page, he will be logged in to the app and can use it without by skipping the form. But what I'm trying to do is to block user from establishing the session until after that Action Required form is complete. Once it is complete user should be automatically logged in (ex. if user was req. to only agree with Terms of Use, he should be logged in without entering a password second time)
Here is the code that I have so far the custom handler:
public class CustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
#Autowired
UserService userService;
public final static String TARGET_URL_SESSION_ATTR_NAME = "target-url";
public CustomLoginSuccessHandler(String defaultTargetUrl) {
setDefaultTargetUrl(defaultTargetUrl);
}
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
HttpSession session = request.getSession();
AuthorityUser authorityUser = (AuthorityUser)authentication.getPrincipal();
String userId = authorityUser.getUserId();
User u = userService.getById(userId);
Boolean changeRequiredDob = u.getChangeRequiredDob();
Boolean changeRequiredPwd = u.getChangeRequiredPwd();
Boolean changeRequiredTou = u.getChangeRequiredTou();
if(changeRequiredDob || changeRequiredPwd || changeRequiredTou){
String targetUrl = determineTargetUrl(request, response);
session.setAttribute(TARGET_URL_SESSION_ATTR_NAME, targetUrl);
getRedirectStrategy().sendRedirect(request, response, "/action-required");
} else {
super.onAuthenticationSuccess(request, response, authentication);
}
}
}
And then once it is successfully complete I'm redirecting user to TARGET_URL_SESSION_ATTR_NAME that was stored to the session.
It would be also helpful to know how to detect and redirect user to the action required screen during the established sessions (if user logged in and later while he is logged in admin sets action required flag on his account).
https://github.com/king-julien/spring-oauth2-customfilter Here is a working sample with Authorization and Resource Server. This Resource Server (vanilla) is a basic stateless application which will not proceed any further until you accept Terms of Service (to accept TOS, Just a do a POST on /tos end point) after authentication.
Create a filter
#Component
public class TosFilter extends OncePerRequestFilter{
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
System.out.println(request.getRequestURI());
// In realworld scenario HelloWorldController.acceptedTOS is a persisted value rather than a static variable
if(!HelloWorldController.acceptedTOS){
//response.sendRedirect("/no-tos");
request.getRequestDispatcher("error-no-tos").forward(request, response);
}
filterChain.doFilter(request,response);
}
}
Register that filter
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
TosFilter rolesFilter;
#Override
public void configure(HttpSecurity httpSecurity) throws Exception{
httpSecurity
.addFilterAfter(rolesFilter, AbstractPreAuthenticatedProcessingFilter.class)
.csrf().disable()
.authorizeRequests().anyRequest().permitAll();
}
}
Annotate your main with #EnableResourceServer.
#SpringBootApplication
#EnableResourceServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The way we solve that is to have the OAuth2 approval page be a single page application.
By default the approval page controller is WhitelabelApprovalEndpoint. We override this by defining out own OauthApproval Controller which overrides "/oauth/confirm_access", so we can add extra stuff to the model. When the approval (jsp) page is loaded, we convert some of the model properties into javascript variables (var token = '${_csrf.token}';), and start an AngularJS application. The approval page can then do whatever it wants (before showing the actual approval form), we just need to build REST endpoints for the different functionalities.
Remember to add #SessionAttributes("authorizationRequest") to the Controller
Instead of AuthenticationSuccessHandler you should use filter:
public class ActionRequirementCheckingFilter extends OncePerRequestFilter {
/* This matcher should not match static resources (js,css etc),
* url`s needed to handle the action and possibly something else,
* depending on your application */
private RequestMatcher matcher;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpSession session = request.getSession();
Boolean actionRequired = false;
/* calculate actual value for actionRequired */
if(matcher.matches(request) && actionRequired){
/* save current request info into session for later use if needed */
response.sendRedirect("/action-required");
} else {
filterChain.doFilter(request, response);
}
}
}
This approach fits all your requirments:
User wont be able to navigate away from it
User will be automatically logged-in after action is complete
It will even work for existing sessions
Only drawback is that session will be actualy created before action is completed, but unless you have a real reason not to do that (which i cant even imaging) this is negligible.
Another way of verifying user access rights during a successfully logged in session is via the filter api
https://www.mkyong.com/spring-mvc/how-to-register-a-servlet-filter-in-spring-mvc/
You can then implement the functionality needed in the doFilter() to verify your rules.

Spring 3 HandlerInterceptor passing information to Controller

I've setup a Spring HandlerInterceptor to add an attribute to the HttpServletRequest to be able to read it from the Controller, sadly this does not seem to work which seems strange to me. Am I doing things wrong? Any idea how to transmit the data from the Interceptor to the Controller?
Here is the simplified code of the two impacted classes
public class RequestInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
request.setAttribute("my-role", "superman");
}
[...]
}
#RestController
#RequestMapping("Test")
public class TestController {
public final Logger logger = LoggerFactory.getLogger(getClass());
#RequestMapping(value = "something")
public void something(HttpServletRequest request) {
logger.info(request.getAttribute("my-role"));
}
[...]
}
The request.getAttribute("my-role") returns null... but does return the excepted value if I read it in the postHandle of the HandlerInterceptor, I feel like I'm missing something...
EDIT : I found out that going thru the session with "request.getSession().setAttribute" works as a charm, still i do not understand why the request itself does not work in this use case.
Can you try with session instead of request like below.
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
...
HttpSession session = request.getSession();
session.setAttribute("attributeName", objectYouWantToPassToHandler);
....
}
In your handler handleRequest method:
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
....
HttpSession session = request.getSession();
objectYouWantToPassToHandler objectYouWantToPassToHandler = session.getAttribute("attributeName");
....
}

Accessing RedirectAttributes in method without RedirectAttributes in signature

Is it possible to access RedirectAttributes in method without RedirectAttributes in signature? For example, when overriding method like one below:
#Override
public void onAuthenticationSuccess(final HttpServletRequest req, final HttpServletResponse res,
final Authentication auth) throws IOException, ServletException {
// add something to RedirectAttributes here
// redirectAttributes.addFlashAttribute("attr", "value");
super.onAuthenticationSuccess(req, res, auth);
}
I'm using spring 3.2.2.RELEASE.
As you can see in the DispatcherServlet class implementation, there are constants:
public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager";
public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP";
public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER";
Spring has a class called RequestContextUtils, which has methods:
public static Map<String, ?> getInputFlashMap(HttpServletRequest request)
public static FlashMap getOutputFlashMap(HttpServletRequest request)
public static FlashMapManager getFlashMapManager(HttpServletRequest request)
The first two methods will give you an access to input and output flash maps respectively.
The last method returns FlashMapManager, which has a number of convinient methods to work with flash attributes. See implementations of this interface for details, specifically AbstractFlashMapManager.
If your goal is "to add indication that the customer has landed on the home page for the first time," then HttpSession may do the trick:
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException
{
...
boolean firstLogin = <determine whether this is the first login>;
<reset condition that caused firstLogin to be true>;
...
HttpSession session = request.getSession(false);
if (session != null) {
session.setAttribute("firstLogin", firstLogin);
}
else {
log.debug("No session");
}
}
// Controller for some page
#RequestMapping(value = <your page>, method = RequestMethod.GET)
public String showPage(<some other args>, HttpSession session)
{
...
Object firstLogin = session.getAttribute("firstLogin");
Assert.isInstanceOf(Boolean.class, firstLogin, "firstLogin");
if ((Boolean)firstLogin) {
<handle first login>;
}
...
return <page>;
}
This works for me. The logic behind this is that the login is in the context of the entire session that, presumably, comprises multiple requests.

Spring Security - Authentication's username is null in AuthenticationSuccessHandler

I have a Customer class which has all the account info.(it does NOT extend Spring's userdetails.User class)
I'm trying to do some stuff after a successful login (e.g. set new last login time). To achieve this I set up a custom AuthenticationSuccessHandler.
In the onAuthenticationSuccess method I try to get the username from the Authentication object. This object however is a User object. If I try to get the username from it I get null.
Can I somehow manage to make the Authority object a Customer object? Or does my Customer class have to extend the User class?
Update
Some more details:
I have my User class. It is completely self written and doesn't implement or extend any interface/class. I do not have a class that implements a UserDetailsService. The <form-login> part of my applicationContext-security.xml looks like this:
<form-login login-page="/index.htm"
authentication-success-handler-ref='authSuccHandler'
authentication-failure-handler-ref='authFailureHandler'
default-target-url='/library/login.htm'
always-use-default-target='true'/>
Theh authSuccHandler looks like this: (The necessary bean definition is in place)
public class PostSuccessfulAuthenticationHandler extends SimpleUrlAuthenticationSuccessHandler
{
#Autowired
private UserService userService;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException
{
userService.trackUserLogin(authentication.getName()); //NullPointerException
super.onAuthenticationSuccess(request, response, authentication);
}
}
The form redirects to j_spring_security_check
Authentication cannot be User, since they don't inherit each other.
If your UserDetailsService produces a custom UserDetails, you should be able to obtain it by calling getDetails() on Authentication.
When the request comes into the authentication success handler, it expects you to redirect to the desired URL. Use the following to redirect to the desired page like home.htm.
This will work definitely!
The modified code is given below. Check it and let me know if you have any issues.
public class PostSuccessfulAuthenticationHandler extends SimpleUrlAuthenticationSuccessHandler
{
#Autowired
private UserService userService;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException
{
userService.trackUserLogin(authentication.getName()); //NullPointerException
response.sendRedirect("home.htm");
//super.onAuthenticationSuccess(request, response, authentication);
}
}
I think the method you are looking for is getPrincipal on Authentication. Then you have to case the object that comes back to your custom class.
User user = (User)authentication.getPrincipal();

Categories