Spring Redirect and delete ModelAttribute - spring

I have a controller:
#GetMapping(value = "/login")
public ModelAndView loginGet (#ModelAttribute(value = "MESSAGE_CUSTOM") String message) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || authentication instanceof AnonymousAuthenticationToken) {
return new ModelAndView("/login");
} else {
return new ModelAndView("redirect:/");
}
}
When I do a redirect, in this case the ModelAttribute is redirected too:
http://localhost:8080/?MESSAGE_CUSTOM=
How do I prevent the ModelAttribute from being redirected?

#GetMapping(value = "/login")
public ModelAndView loginGet (#ModelAttribute(value = "MESSAGE_CUSTOM") String message) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || authentication instanceof AnonymousAuthenticationToken) {
return new ModelAndView("/login");
} else {
RedirectView redirectView = new RedirectView("/");
redirectView.setExposeModelAttributes(false);
return new ModelAndView(redirectView);
}
}
http
http://localhost:8080/

Related

SecurityContextHolder authentication object not available to subsequent requests from the client

Inside getUserObject() method we are not able to get Authentication object. It's available for 1st request only. But its setting to null for subsequent requests from client. So please help me to configure it properly so that its available for all the requests calls.
I am not sure how to configure inside configure method in AuthConfig.java so that authentication object would be available for all the requests chain
AuthConfig.java:
#Configuration
#EnableWebSecurity
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/callback", "/", "/auth0/authorize", "/resources/**", "/public/**", "/static/**",
"/login.do", "/logout.do", "/thankYou.do", "/customerEngagement.do",
"/oldCustomerEngagement.do", "/registerNew.do", "/forgotPassword.do", "/checkMongoService.do",
"/reset.do", "/rlaLogin.do", "/fnfrefer.do", "/thankYouLeadAggregator.do", "/referral")
.permitAll()
.anyRequest().authenticated().and().
logout()
.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler());
}
------------------------------------------------------------------------------
AuthController.java:
#RequestMapping(value = "/callback", method = RequestMethod.GET)
public void callback(HttpServletRequest request, HttpServletResponse response)
throws IOException, IdentityVerificationException {
try {
Tokens tokens = authenticationController.handle(request, response);
DecodedJWT jwt = JWT.decode(tokens.getIdToken());
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
Authentication auth = new UsernamePasswordAuthenticationToken(jwt.getSubject(), jwt.getToken(), grantedAuths);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(auth);
response.sendRedirect(config.getContextPath(request) + "/loancenter/home.do");
} catch (Exception e) {
LOG.info("callback page error");
response.sendRedirect(config.getContextPath(request) + "/loancenter");
}
}
--------------------------------------------------------------------------------
HomeController.java:
#Controller
public class DefaultController implements InitializingBean {
#RequestMapping(value = "home.do")
public ModelAndView showCustomerPage(HttpServletRequest req, HttpServletResponse res, Model model) {
ModelAndView mav = new ModelAndView();
try {
User user = getUserObject(req);
if(user==null) {
LOG.info("User not found in session");
mav.setViewName(JspLookup.LOGIN);
return mav;
}
} catch (Exception e) {
LOG.error("Exception in Home page ", e);
}
return mav;
}
protected User getUserObject(HttpServletRequest request) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
LOG.info("authentication::{}", authentication);
User user = null;
if (authentication == null) {
return user;
}
if (authentication.getPrincipal() instanceof User) {
user = (User) authentication.getPrincipal();
LOG.info("User already authenticated and logging :{}", user.getEmailId());
sendUserLoginEmailToLO(user);
} else {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
DecodedJWT jwt = JWT.decode(token.getCredentials().toString());
user = userProfileDao.findByUserEmail(jwt.getClaims().get("email").asString());
if (user != null) {
LOG.info("First time authentication:{}", user.getEmailId());
boolean auth0EmailVerified = jwt.getClaims().get("email_verified").asBoolean();
LOG.info("First time authentication email verified flag from auth0:{}", auth0EmailVerified);
LOG.info("First time authentication email verified flag from nlc:{}", user.getEmailVerified());
if (BooleanUtils.isFalse(user.getEmailVerified()) && auth0EmailVerified) {
LOG.info("Email is verified in Auth0, updating email_verified flag to true in DB for userId: {}",
user.getId());
userProfileDao.verifyEmail(user.getId());
LOG.info("First time authentication updated email verified flag in nlc db:{}", user.getEmailId());
}
if (user.getNewEmailVerified() != null && BooleanUtils.isFalse(user.getNewEmailVerified())) {
LOG.info("The user is verifying his email: set his verified to true");
userProfileDao.verifyNewEmail(user.getId());
}
Authentication auth = new UsernamePasswordAuthenticationToken(user, jwt.getToken(),
token.getAuthorities());
messageServiceHelper.checkIfUserFirstLogin(user);
LOG.info("Authentication provided for user : {}", user.getEmailId());
LOG.debug("Auth object constructed : {}", auth);
SecurityContextHolder.getContext().setAuthentication(auth);
HttpSession session = request.getSession(true);
session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
sendUserLoginEmailToLO(user);
}
}
return user;
}
}

How to add accessToken authentication in Zuul API Gateway?

I would like to authenticate all the requests at Zuul Gateway. i,e., each API request will send accessToken and zuul should authenticate the accessToken.
I have implemented this by extending ZuulFilter.
My code is below:
#Component
public class ZuulPreFilter extends ZuulFilter {
#Autowired
private DiscoveryClient discoveryClient;
#Override
public String filterType() {
return PRE_TYPE;
}
#Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER + 1;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String accessToken = request.getParameter("accessToken");
if (accessToken == null || accessToken.isEmpty()) {
return sendUnauthorizedResponse(ctx, request, "Required AccessToken");
}
List<ServiceInstance> instances = this.discoveryClient.getInstances("security_service");
if (instances != null && instances.size() > 0) {
ServiceInstance securityService = instances.get(0);
String authAccessTokenAPI = securityService.getUri().toString().concat("/users/?accessToken=")
.concat(accessToken);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<ValidationResponse> response = restTemplate.getForEntity(authAccessTokenAPI,
ValidationResponse.class);
ValidationResponse authUserResponse = response.getBody();
if (authUserResponse == null || !authUserResponse.isSuccess()) {
return sendUnauthorizedResponse(ctx, request, "Invalid AccessToken");
}
}
return null;
}
private Object sendUnauthorizedResponse(RequestContext ctx, HttpServletRequest request, String responseMessage) {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
ValidationResponse validationResponse = new ValidationResponse();
validationResponse.setSuccess(false);
validationResponse.setMessage(responseMessage);
String contentType = request.getContentType();
if (contentType == null) {
contentType = MediaType.APPLICATION_JSON;
}
// To handle Json contentType
if (contentType.equalsIgnoreCase(MediaType.APPLICATION_JSON)) {
ObjectMapper mapper = new ObjectMapper();
try {
String responseBody = mapper.writeValueAsString(validationResponse);
ctx.setResponseBody(responseBody);
ctx.getResponse().setContentType(MediaType.APPLICATION_JSON);
} catch (JsonProcessingException e) {
}
}
// To handle XML contentType
if (contentType.equalsIgnoreCase(MediaType.APPLICATION_XML)) {
String responseBody = XmlUtil.marshal(validationResponse);
ctx.setResponseBody(responseBody);
ctx.getResponse().setContentType(MediaType.APPLICATION_XML);
}
return ctx;
}
}
I felt its NOT an effective way. Is there any other way to implement this scenario ?
Thanks.

Spring Boot rendering index page after authentication

I have implemented SSO SAML using Spring Security. In my Spring Boot project I have the following controller which basically redirects a user to an idP login page it then generates a JWT token based on a successful login. This JWT token is then forwarded to the index page as a header. But I cant seem to get this to work properly.
Auth Controller,
#Controller
public class AuthController {
private static final Logger log = LoggerFactory.getLogger(UserAccountResource.class);
#Inject
private TokenProvider tokenProvider;
/**
* Given that a user is already authenticated then generate a token
*
* #return the ResponseEntity with status 200 (OK) and with body of the updated {#link JWTToken} jwt token
*/
#RequestMapping(value = "/auth/login")
public String login(HttpServletResponse response) throws Exception {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new UnauthorizedException();
} else {
try {
final SAMLCredential credential = (SAMLCredential) authentication.getCredentials();
final DateTime dateTime = credential.getAuthenticationAssertion()
.getIssueInstant()
.toDateTime(DateTimeZone.forTimeZone(TimeZone.getDefault()));
String jwt = tokenProvider.createToken(authentication, dateTime.getMillis(), false);
response.addHeader(JWTConfigurer.AUTHORIZATION_HEADER, "Bearer " + jwt);
log.debug("Generated jwt {}", jwt);
log.debug("SAMLCredential {}", credential);
return "forward:/";
} catch (Exception e) {
throw new UnauthorizedException(e);
}
}
}
}
WebMvcConfigurerAdapter is as follows,
#Configuration("webConfigurer")
public class WebConfigurer extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("forward:/index.html");
}
}
As far as SSO via SAML goes everything works great. The user gets redirected to the idP login e.t.c. What I cant figure out is why the forward isn't work as expected.
All my UI (Angular 4.x) is initiated with index.html.
When I tested this I can see it being forwarded to / however no headers come through.
What I did in the end is to separate the login and jwt generation to two APIs calls which worked great.
#GetMapping("/login")
public String samlLogin() throws Exception {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new UnauthorizedException("Unable to get the SAML authentication information");
} else {
try {
final SAMLCredential credential = (SAMLCredential) authentication.getCredentials();
if (credential == null) {
throw new UnauthorizedException("Not valid SAML credentials");
}
return "forward:/";
} catch (Exception e) {
throw new UnauthorizedException(e);
}
}
}
#GetMapping("/jwt")
public ResponseEntity<String> generateJWT(HttpServletResponse response) throws Exception {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new UnauthorizedException("Unable to get the SAML authentication information");
} else {
try {
final SAMLCredential credential = (SAMLCredential) authentication.getCredentials();
if (credential == null) {
throw new UnauthorizedException("Not valid SAML credentials");
}
final DateTime dateTime = credential.getAuthenticationAssertion()
.getIssueInstant()
.toDateTime(DateTimeZone.forTimeZone(TimeZone.getDefault()));
String jwt = tokenProvider.createToken(authentication, dateTime.getMillis(), false);
response.addHeader(JWTConfigurer.AUTHORIZATION_HEADER, "Bearer " + jwt);
log.debug("Generated jwt {} for SAML authentication", jwt);
return new ResponseEntity<>(jwt, HttpStatus.OK);
} catch (Exception e) {
throw new UnauthorizedException(e);
}
}
}

Spring MVC redirect failed

I wanted to do a login page with Spring MVC + Mybatis, the controllers are as below:
#Controller
public class LoginController {
private final UserService userService;
#Autowired
public LoginController(#Qualifier("userService") UserService userService) {
this.userService = userService;
}
#RequestMapping(value = "/login")
public ModelAndView Login(String username, String password,
ModelAndView mv, HttpSession session) {
User user = userService.login(username, password);
// **This is where problems occured**
if (user != null) {
session.setAttribute("user", user);
mv.setView(new RedirectView("/main"));
} else {
mv.addObject("message","Login failed.");
mv.setViewName("loginForm");
}
return mv; // with correct value, but responses 404 error
}
#RequestMapping(value = "/loginForm")
public String LoginForm() {
return "loginForm";
}
#RequestMapping(value = "/main")
public String Main(Model model) {
return "main";
}
}
I have two jsp files:
/WEB-INF/jsp/loginForm.jsp
/WEB-INF/jsp/main.jsp
Whether I submit the login form correctly or not, there is a 404 error:
HTTP Status 404 - /WEB-INF/jsp/login.jsp
Running in debug mode, the mv was returned with "/main" correctly, so it confused me.
Code below works, but i cant add error message to view anymore.
#RequestMapping(value = "/login")
public String Login(String username, String password, HttpSession session) {
User user = userService.login(username, password);
if (user != null) {
session.setAttribute("user", user);
return "redirect:/main";
} else {
session.setAttribute("message","Login failed");
return "redirect:/loginForm";
}
}
i have face same issue but it's help for me
ModelAndView model = new ModelAndView();
if (user != null) {
session.setAttribute("user", user);
model = new ModelAndView("/main");
} else {
mv.addObject("message","Login failed.");
model = new ModelAndView("/loginForm");
}
return model;

How to know that a session is expired?

I set values to the session object in the method of a controller after success of login :
#RequestMapping(value = "/", method = RequestMethod.POST)
public ModelAndView processLogin(Model model, HttpServletRequest request, HttpSession session, #RequestParam String login, #RequestParam String pwd) {
if ( utilisateurDao.verifierLoginUser(login) ) {
if ( utilisateurDao.verifierUser(login, pwd) ) {
HashMap<String, String> criteres = new HashMap<String, String>();
criteres.put("user_login", login);
criteres.put("user_passwd", pwd);
List<Utilisateur> users = utilisateurDao.lireParCritere(criteres);
session.setAttribute("user_code", ((Utilisateur)users.get(0)).getUser_code());
session.setAttribute("menu", menuDao.afficherMenuParUtilisateur((Integer)session.getAttribute("user_code"), env, request, session));
criteres.clear();
users.clear();
criteres.put("user_code", String.valueOf(session.getAttribute("user_code")));
users = utilisateurDao.lireParCritere(criteres);
session.setAttribute("user_names", ((Utilisateur)users.get(0)).getNoms());
session.setAttribute("logout_menu", env.getProperty("menu.logout"));
return new ModelAndView("redirect:/accueil");
} else {
ModelAndView modelViewLogin = new ModelAndView("redirect:/");
modelViewLogin.addObject("e", "p").addObject("l", login);
return modelViewLogin;
}
} else {
ModelAndView modelViewLogin = new ModelAndView("redirect:/");
modelViewLogin.addObject("e", "l");
return modelViewLogin;
}
}
Then I opened the app inactive for some minutes. After that I went to the "accueil" path. Then the menu was not shown anymore ! The menu was got from session. So how to know that the session is expired and where is the convenient place to test it ?
By default in spring security session is stored in SessionRegistry.
By using SecurityContext you can get this info in your controller code.
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
If you want to be notified when session has expired or person logged out you can always register listener on SessionDestroyedEvent- documentation.
example:
#Component
public class LogoutListener implements ApplicationListener<SessionDestroyedEvent> {
#Override
public void onApplicationEvent(SessionDestroyedEvent event) {
//do your stuff here
}
}
Its also worth to refer to spring docs for that subject.
You can make a Interceptor,
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter
In this interceptor you can control the HttpServletRequest
and check if obj exists into them and then you can throw to a new SessionExpiredException and catch with #ExceptionMapper (https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc)
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if (request.getSession().getAttribute("user")==null) {
throw new SessionExpiredException();
}
return true;
}
I check like below. I think it might be help.
public boolean isUserLoggedIn(HttpServletRequest request) throws IOException {
SecurityContext securityContext = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
if(securityContext != null) {
Authentication authentication = securityContext.getAuthentication();
if(null != authentication && authentication.isAuthenticated() != true)
return false;
else
return true;
} else {
return false;
}
}

Resources