I'm new using spring mvc in general. I'm generating login page and my problem is that it always redirects me to the notLoggedIn prompt after I've tried to log in.
The controller:
#RequestMapping(value="/login", method= RequestMethod.POST) //login
public String logIn(HttpServletRequest request, HttpServletResponse response, ModelMap map) {
HttpSession session= request.getSession();
request.getSession().setAttribute("isLoggedIn", "true");
String uname=request.getParameter("userid");
String pword=request.getParameter("password");
boolean exists=logInService.checkLogIn(uname, pword);
if(exists){
session.setAttribute("userid", uname);
return "Users"; //return to next success login jsp
} else {
return "Interface2"; //return to Invalid username and password jsp
}
}
The interceptor:
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session= request.getSession();
if(session.getAttribute("userid")!=null && session.getAttribute("isLoggedIn")!=null ){
System.out.println("Logged In");
}
else{
response.sendRedirect(request.getContextPath()+"/modulename/notLoggedIn");
System.out.println("Not logged in");
return false;
}
return true;
}
Your interceptor blocks every http request and does some check but it should actually allow and not check for login http request. Following changes are just to get the use case work. Refer note at the bottom for suggestions.
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session= request.getSession();
if(session.getAttribute("userid")!=null && session.getAttribute("isLoggedIn")!=null ){
//user has already logged in . so therefore can access any resource
System.out.println("Logged In");
return true;
}
//if code reaches here means that user is not logged in
//allow login http request. modify checks accordingly. like you can put strict equals.
if (request.getRequestURI().endsWith("/login")){
//user is not logged in but is trying to login. so allow only login requests
return true;
}
else{
//user is not logged in and is trying to access a resource. so redirect him to login page
response.sendRedirect(request.getContextPath()+"/modulename/notLoggedIn");
System.out.println("Not logged in");
return false;
}
}
Note: You can reorder your login http request check to avoid login request for already logged in user.
Related
This is my LoginController
#PostMapping("/login")
#Transactional
public String login(#Valid #ModelAttribute("login") LoginRequest loginRequest, BindingResult result, HttpServletResponse response, Model model) {
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
boolean no = authentication.isAuthenticated();
if (!no) {
model.addAttribute("loginError", true);
logger.info("User is not auth");
return "form_login";
}
SecurityContextHolder.getContext().setAuthentication(authentication);
UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal();
ResponseCookie jwtCookie = jwtUtils.generateJwtCookie(user);
response.addHeader(HttpHeaders.SET_COOKIE, jwtCookie.toString());
model.addAttribute("login", loginRequest);
return "redirect:/api/test/homePage";
}
As you can see, I want to check is user authenticated, I mean does user insert good credentials. You can see that I tried like this:
boolean no = authentication.isAuthenticated();
if (!no) {
model.addAttribute("loginError", true);
logger.info("User is not auth");
return "form_login";
}
But that return me a Whitelabel Error Page with 403 Forbidden, while I want to display error message on Thymeleaf user.
This is in thymeleaf:
<p th:if="${loginError}" class="error">Wrong user or password</p>
How I can check user credentials that he insert so if he enter wrong credentials show him wrong password/username?
Also my logger.info("User is not auth"); is not being printed even when user enter wrong credentials but I get 403 Forbidden.
I am trying to input some more "accurate" error handling for invalid logins.
The three main objectives: invalid password, account disabled, invalid email.
The current calling hierarchy is the following:
Attempted login requests
#Override // THIS OVERRIDES THE DEFAULT SPRING SECURITY IMPLEMENTATION
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String email = request.getParameter("email");
String password = request.getParameter("password");
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(email, password);
return authManager.authenticate(authToken);
}
This calls another override method where I tried to insert error handling because it has access to the userRepo and object. The issue here is if the AccountLockedException or fails on email finding or password verification, it will always reutrn a 403 and no indication of the thrown exception.
#SneakyThrows
#Override // THIS OVERWRITES THE DEFAULT SPRING SECURITY ONE
public UserDetails loadUserByUsername(String email){
User user = findUserByEmail(email);
if ( user != null){
if (user.isEnabled()){
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
user.getRoles().forEach(role -> { authorities.add(new SimpleGrantedAuthority(role.getName()));});
sucessfulLogin(user);
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities);
}
else { throw new AccountLockedException("Account disabled"); }
}
}
However, what I have found this previous method on throwing will call this additional override method (in the same class as the attempted authentication)
#Override // DO SOMETHING WITH THIS TO PREVENT BRUTE FORCE ATTACKS WITH LIMITED NUMBER OF ATTEMPTS IN A TIME-FRAME
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
System.out.println("aaa");
super.unsuccessfulAuthentication(request, response, failed);
}
Though, at this point it will display the following:
this option gets shown when the password is incorrect.
this option gets shown when the account is disabeld.
this option gets shown when the email is incorrect.
My question is. Firstly how do I appropriately distinguish between these errors and secondly send appropriate http responses based on these errors?
if (failed != null) {
if (failed.getMessage() == "AccountLockedException") {
response.setStatus(403);
} // if account is disabled
else if (failed.getMessage() == "EntityNotFoundException") {
response.setStatus(400);
} // if email incorrect
else if (failed.getMessage() == "Bad credentials") {
response.setStatus(400);
} // if password incorrect
else {
System.out.println("some kind of other authentication error");
response.setStatus(418); // some random error incase this ever happens
}
I have an application that does authentication via oauth.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpServletResponse httpResp = (HttpServletResponse) response;
// Check if already logged in
if (getUser(httpReq) != null) {
chain.doFilter(request, response);
return;
}
// Try to parse auth response
if (procAuthResponse(httpReq)) {
chain.doFilter(request, response);
return;
}
// Go to auth server
sendAuthRequest(httpReq, httpResp);
}
This works fine.
In the method procAuthResponse I am paring the response from the server and to this.
HttpSession session = request.getSession();
session.setAttribute(USER_PRINCIPLE_ATR, userInfo);
It works also well, but there is a session scoped class with the method getCurrent user, that is used by servlets.
public UserInfo getCurrentUser() {
HttpSession session = getHttpSession();
if (session == null) {
LOG.warn("Method getCurrentUser: unable to find a session");
return null;
}
Object user = session.getAttribute(OAuthLoginFilter.USER_PRINCIPLE_ATR);
if (!(user instanceof UserInfo)) {
LOG.warn(String.format("Method getCurrentUser, wrong type for attribute %s", OAuthLoginFilter.USER_PRINCIPLE_ATR));
return null;
}
currentUser = (UserInfo) user;
return currentUser;
}
This method gets called multiple times and it turnes out that on the first call everything works as expected and after that the getHttpSession() returns a different session that does not contain any information that is set in the filter class. It is not a new session every time, the session without the needed information is always the same.
Code of getHttpSession()
private HttpSession getHttpSession() {
Object request = FacesContext.getCurrentInstance().getExternalContext().getRequest();
if (!(request instanceof HttpServletRequest)) {
LOG.warn("not a valid http request");
return null;
}
HttpServletRequest hreq = (HttpServletRequest) request;
return hreq.getSession(false);
}
Do you have any idea why this happens?
Thx for your help
There was still an old filter class, not configured in the web.xml, but annotated with #WebFilter("/*").
I deleted this file and now everything works as expected.
in spring security:
i think with tow way logout called: when a session timeout occurred or a user logout itself...
anyway in these ways , destroyedSession called in HttpSessionEventPublisher and SessionRegistry remove SessionInformation from sessionIds list...
when i use below method for force logout specific user , this method just "expired" SessionInformation in SessionRegistry. now when i get all online user "getAllPrincipals()" from SessionRegistry, the user that session expired, is in the list!
#Override
public boolean forceLogOut(int userId){
for (Object username: sessionRegistry.getAllPrincipals()) {
User temp = (User) username;
if(temp.getId().equals(userId)){
for (SessionInformation session : sessionRegistry.getAllSessions(username, false)) {
session.expireNow();
}
}
}
return true;
}
how can i logout 'specific user' or 'sessionId' that session object remove from "Web Server" and "Session Registry" ?
i googling and found HttpSessionContext in Servlet API that can get HttpSession from specific sessionId. and then invalidate session. but i think this method is not completely useful!
(note. this class is deprecated!)
what is the best way? Whether I'm wrong?
Try like this:
#RequestMapping(value="/logout", method = RequestMethod.GET)
public String logoutPage (HttpServletRequest request, HttpServletResponse response){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
//new SecurityContextLogoutHandler().logout(request, response, auth);
persistentTokenBasedRememberMeServices.logout(request, response, auth);
SecurityContextHolder.getContext().setAuthentication(null);
}
return "redirect:/login?logout";
}
To logout specific session Id check that link:
how to log a user out programmatically using spring security
I need to do some validations on the login form before calling the authenticationManager for authentication. Have been able to achieve it with help from one existing post - How to make extra validation in Spring Security login form?
Could someone please suggest me whether I am following the correct approach or missing out something? Particularly, I was not very clear as to how to show the error messages.
In the filter I use validator to perform validations on the login field and in case there are errors, I throw an Exception (which extends AuthenticationException) and encapsulate the Errors object. A getErrors() method is provided to the exception class to retrieve the errors.
Since in case of any authentication exception, the failure handler stores the exception in the session, so in my controller, I check for the exception stored in the session and if the exception is there, fill the binding result with the errors object retrieved from the my custom exception (after checking runtime instance of AuthenticationException)
The following are my code snaps -
LoginFilter class
public class UsernamePasswordLoginAuthenticationFilter extends
UsernamePasswordAuthenticationFilter {
#Autowired
private Validator loginValidator;
/* (non-Javadoc)
* #see org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
#Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
Login login = new Login();
login.setUserId(request.getParameter("userId"));
login.setPassword(request.getParameter("password"));
Errors errors = new BeanPropertyBindingResult(login, "login");
loginValidator.validate(login, errors);
if(errors.hasErrors()) {
throw new LoginAuthenticationValidationException("Authentication Validation Failure", errors);
}
return super.attemptAuthentication(request, response);
}
}
Controller
#Controller
public class LoginController {
#RequestMapping(value="/login", method = RequestMethod.GET)
public String loginPage(#ModelAttribute("login") Login login, BindingResult result, HttpServletRequest request) {
AuthenticationException excp = (AuthenticationException)
request.getSession().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
if(excp != null) {
if (excp instanceof LoginAuthenticationValidationException) {
LoginAuthenticationValidationException loginExcp = (LoginAuthenticationValidationException) excp;
result.addAllErrors(loginExcp.getErrors());
}
}
return "login";
}
#ModelAttribute
public void initializeForm(ModelMap map) {
map.put("login", new Login());
}
This part in the controller to check for the instance of the Exception and then taking out the Errors object, does not look a clean approach. I am not sure whether this is the only way to handle it or someone has approached it in any other way? Please provide your suggestions.
Thanks!
#RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView signInPage(
#RequestParam(value = "error", required = false) String error,
#RequestParam(value = "logout", required = false) String logout) {
ModelAndView mav = new ModelAndView();
//Initially when you hit on login url then error and logout both null
if (error != null) {
mav.addObject("error", "Invalid username and password!");
}
if (logout != null) {
mav.addObject("msg", "You've been logged out successfully.");
}
mav.setViewName("login/login.jsp");
}
Now if in case login become unsuccessfull then it will again hit this url with error append in its url as in spring security file you set the failure url.
Spring security file: -authentication-failure-url="/login?error=1"
Then your URl become url/login?error=1
Then automatically signInPage method will call and with some error value.Now error is not null and you can set any string corresponding to url and we can show on jsp using these following tags:-
<c:if test="${not empty error}">
<div class="error">${error}</div>
</c:if>