Spring Security 5 authentication always return 302 - spring

I'm using Spring-Security 5 to secure my web app. I access /login.jsp and fill in username and password, and then click "Log in" to submit the form, and then was redirected to /login.jsp. I see the reponse status code of that http traffic in fiddler is 302.
SecurityConfig class:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private DataSource dataSource;
#Autowired
protected SecurityConfig(DataSource dataSource
) {
this.dataSource = dataSource;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.jsp")
.loginProcessingUrl("/login")
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select name userName, password, enabled from user where name=?")
.authoritiesByUsernameQuery("select name userName 'ROLE_USER' from user where name=?")
;
}
}
login.jsp:
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<%# taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<c:url value="/login" var="loginUrl"/>
<form action="${loginUrl}" method="post"> 1
<c:if test="${param.error != null}"> 2
<p>
Invalid username and password.
</p>
</c:if>
<c:if test="${param.logout != null}"> 3
<p>
You have been logged out.
</p>
</c:if>
<p>
<label for="username">Username</label>
<input type="text" id="username" name="username"/> 4
</p>
<p>
<label for="password">Password</label>
<input type="password" id="password" name="password"/> 5
</p>
<button type="submit" class="btn">Log in</button>
</form>
</body>
</html>

This is because spring default authentication success handler looks for a url to redirect.
What one can do is use a custom AuthenticationSuccessHandler
i have used below and no redirects are happening.
public class AppAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{
protected void handle(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
}
}
Then define the bean and give it in the configure method for security
#Bean
public AuthenticationSuccessHandler appAuthenticationSuccessHandler(){
return new AppAuthenticationSuccessHandler();
}
Configure method
http
.authorizeRequests()
.antMatchers("/login*")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.successHandler(appAuthenticationSuccessHandler());

I had this problem until I turned csrf-check off by including .csrf().disable() in configure (HttpSecurity) method.
If you don't have it off then provide csrf token as hidden form field.
... though I see that you have it off disabled

the "loginPage url" same of the "form action"
show me code
java config:
http.formLogin().loginPage("/login.html")
html
<form action="/login.html" method="post">
you just need write controller for "/login.html", by http GET method, Leave the rest to “spring”
docs: https://docs.spring.io/spring-security/site/docs/5.3.6.RELEASE/reference/html5/#servlet-authentication-form
the UsernamePasswordAuthenticationFilter match /login.html by http POST method
My English is not good, Hope I can help you

I don't known if this issue is always active but if this can help someone...
What's works for me was to replace
.formLogin()
by
.httpBasic();
in my WebSecurityConfigurerAdapter class.
So my security config looks like this :
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login", "/actuator/**", "/clients/refresh", "/oauth/token/revokeById/**", "/tokens/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.httpBasic();
}

Use successHandler to set the referer true. This does the trick for me. Else I am also getting 302.
In securityConfig need to add the below code.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login*")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.successHandler(new RefererRedirectionAuthenticationSuccessHandler ());
}
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
public class RefererRedirectionAuthenticationSuccessHandler extends
SimpleUrlAuthenticationSuccessHandler {
public RefererRedirectionAuthenticationSuccessHandler() {
super();
setUseReferer(true);
}
}
}
Check the below link:
http://www.baeldung.com/spring-security-redirect-login

Related

Spring MVC/Security - Users still can access login page after successful login

I am very new to Spring.
I am working with version 4.3.9.RELEASE of Spring MVC, 4.2.3.RELEASE of Spring Security.
I am using the built in login of spring with a little customization, here is my configure
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService myUserService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(myUserService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.rememberMe()
.alwaysRemember(true)
.and()
.logout()
.permitAll();
}
}
Why users can access login page after they logged in successfully? I tried to learn from the questions that are the same like mine, but none of them works with me.
This solution doesn't work with me:
<sec:authorize access="isAuthenticated()">
<% response.sendRedirect(request.getContextPath()); %>
</sec:authorize>
I am using Apache Tiles, I have is_authenticated.jsp with that part. and this is the tiles.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
"http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
<definition name="base" template="/WEB-INF/layouts/page.jsp">
<put-attribute name="pageHeader" value="/WEB-INF/layouts/page_header.jsp"></put-attribute>
<put-attribute name="pageFooter" value="/WEB-INF/layouts/page_footer.jsp"></put-attribute>
</definition>
<definition name="login" extends="base">
<put-attribute name="isAuthenticated" value="/WEB-INF/views/is_authenticated.jsp"></put-attribute>
<put-attribute name="pageBody" value="/WEB-INF/views/login.jsp"></put-attribute>
</definition>
<definition name="home" extends="base">
<put-attribute name="isAuthenticated" value=""></put-attribute>
<put-attribute name="pageBody" value="/WEB-INF/views/home.jsp"></put-attribute>
...
</tiles-definitions>
and here is page.jsp
<!DOCTYPE>
<html>
<head>
<t:insertAttribute name="isAuthenticated"></t:insertAttribute>
...
</head>
<body>
<!-- Page Layout HTML -->
<header id="pageHeader">
<t:insertAttribute name="pageHeader"></t:insertAttribute>
</header>
<main id="pageBody">
<t:insertAttribute name="pageBody"></t:insertAttribute>
</main>
...
</body>
</html>
The is_authenticated.jsp is included and rendered but it doesn' work, it does work only if I put the block inside the page.jsp itself, which looks wrong as you see, but it doesn't work when included from another jsp file.
Another solution, handling this issue from login controller, but this is not available in my case, because I didn't use any controller to handle login process.
Should I do?
The custom login controller will be more secure than the default one in Spring?
Update1
I tried to use the default login feature of spring:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService myUserService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(myUserService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
But I found that after successful login, user can still access login page.
So I am guessing I need to have a method in LoginController to accomplish this.
I had the same problem and I solved it by using access() method:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/login", "/signup", "/")
.access("hasRole('ANONYMOUS')")
.antMatchers("/test").access("permitAll()")
.antMatchers("/user/**").access("hasRole('ADMIN')")
.anyRequest().authenticated()
.antMatchers()
.access("hasRole('USER')").anyRequest().fullyAuthenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.successHandler(loginSuccessHandler()).permitAll()
.failureHandler(loginFailureHandler())
.and()
.logout()
.permitAll()
.logoutSuccessUrl("/");
}
private AuthenticationSuccessHandler loginSuccessHandler() {
return (request, response, authentication) ->{
response.sendRedirect("/home");
};
}
private AuthenticationFailureHandler loginFailureHandler() {
return (request, response, exception) -> {
response.sendRedirect("/");
};
}
I think I found an answer.
Since I configured configure method as this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.rememberMe()
.alwaysRemember(true)
.and()
.logout()
.permitAll();
}
I was supposed to have addViewControllers in my implementation of WebMvcConfigurerAdapter.
So I tried to remove this method and instead create a LoginController which only have login() method for GET requests that handle the redirect if the user is authenticated.
#Controller
public class LoginController {
#RequestMapping(value="/login", method=GET)
public String login(Principal principal) {
return principal == null ? "login" : "redirect:/";
}
}

Logout doesn't work with Spring Boot and Spring Security

This is my code using Spring Boot and Spring Security. The problem is when I used to logout (using Thyemleaf) the logout doesn't work for me.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private DataSource dataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select username as principal, password as credentials,active from users where username=?")
.authoritiesByUsernameQuery("select username as principal,roles as role from users_roles where username=?")
.rolePrefix("ROLE_")
.passwordEncoder(new Md5PasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login");
http
.authorizeRequests()
.antMatchers("/index1").permitAll();
http
.authorizeRequests()
.antMatchers("/user").hasRole("USER")
.and()
.logout();
http
.authorizeRequests()
.antMatchers("/adpage").hasRole("ADMIN");
http
.exceptionHandling().accessDeniedPage("/403");
http
.logout().permitAll();
}
}
Link using Thyemleaf:
<li><a th:href="#{/login?logout}">logout</a></li>
Try doing something like this.
<form th:action="#{/logout}" method="post">
<input type="submit" value="Log out"/>
</form>
Spring security logout Url is POST only. You can support Non-POST logout by changing your Java Configuration
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
}
this way you can logout user using GET request
<li><a th:href="#{/logout}">logout</a></li>
Try the following instead:
http
.formLogin()
.loginPage("/login")
.failureUrl("/login?login_error=true")
.loginProcessingUrl("/j_spring_security_check") //if needed
.and()
.authorizeRequests()
.antMatchers("/index1").permitAll()
.antMatchers("/user").hasRole("USER")
.antMatchers("/adpage").hasRole("ADMIN")
.and()
.exceptionHandling().accessDeniedPage("/403")
.and()
.logout()
.logoutSuccessUrl("/index") //or whatever page you want
.logoutUrl("/logout") //thinking this is what you need
.permitAll();
And your link would be:
<li><a th:href="#{/logout}">logout</a></li>

Spring Security blocking Rest Controller

My app has Spring boot 1.3.2 and I'm trying use Spring MVC with Spring Security.
I have administration panel under http://localhost:8080/admin and my page content for common users under http://localhost:8080/
If You are trying to open an admin panel (http://localhost:8080/admin) You have to log in, if You are common just enter http://localhost:8080/ and have fun no log in required.
My Security config class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin")
.password("password")
.roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/**").permitAll()
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login");
}
}
Config above let me to require login from /admin
But I have some problem with Admin panel features.
This is Controller I'm trying to request with POST from admin panel:
#RestController
#RequestMapping("/admin/v1")
public class AdminController {
#RequestMapping(value = "/logout", method = RequestMethod.POST)
public String logout(HttpServletRequest request, HttpServletResponse response) {
String hello = "hi!";
return hello;
}
}
So I can log in, browser render Admin panel for me but when I'm clicking logout button which request POST logout method from Controller above. App tells me 403 Forbidden
Can anybody tell me what I'm doing wrong?
Most probably the 403 Forbidden error is because the spring by default enable csrf protection.
You can disable csrf in configuration or Include the CSRF Token in the POST method.
Disable csrf in config:
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/**").permitAll()
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.and()
.logout()
.logoutSuccessUrl("/admin/v1/logout");
Include the CSRF Token in Form Submissions:
<c:url var="logoutUrl" value="/admin/v1/logout"/>
<form action="${logoutUrl}" method="post">
<input type="submit" value="Log out" />
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>

spring security 4 custom login page

I would like to create custom pure html/js login page in Spring Security.
I use Spring Boot 1.2.5.RELEASE
I defined an application and configuration:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#Configuration
#EnableWebSecurity
#EnableWebMvcSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("a").password("a").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // DISABLED CSRF protection to make it easier !
.authorizeRequests()
.antMatchers("/", "/login.html").permit
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")
.permitAll()
.and()
.logout()
.permitAll()
.logoutUrl("/logout")
.logoutSuccessUrl("/");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
My login page looks like that (copied from default page!)
<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
<table>
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td> </tr>
</table>
</form></body></html>
But I still have: AUTHORIZATION_FAILURE
Is it possible to create pute html login page (without jsp, thymeleaf, etc.) ?
What do I do wrong in my code ?
You configured your login page to be at /login.html (using loginPage("/login.html")). This will also change the location to which you need to post the credentials to login. The documentation states:
If "/authenticate" was passed to this method [loginPage(String)] it update the defaults as
shown below:
/authenticate GET - the login form
/authenticate POST - process the credentials and if valid authenticate the user
/authenticate?error GET - redirect here for failed authentication attempts
/authenticate?logout GET - redirect here after successfully logging out
In order to make the login work, you need to make login.html post the credentials to /login.html instead of /login.

How to configure Spring 4.0 with spring boot and spring security openId

I'm trying to get a Spring 4.0 boot application up and running with Spring Security OpenId. I'm using the standard way to bootstrap a Spring boot app:
#Configuration
#ComponentScan("x.y.z")
#EnableAutoConfiguration
#Import({SecurityConfig.class})
public class ServiceRegistryStart extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(ServiceRegistryStart.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
application.sources(getClass());
return application;
}
}
The SecurityConfig.class looks like this (Influenced by the "openid-jc sample project in Spring security):
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.openidLogin()
.loginPage("/login.html")
.permitAll()
.authenticationUserDetailsService(new CustomUserDetailsService())
.attributeExchange("https://www.google.com/.*")
.attribute("email")
.type("http://axschema.org/contact/email")
.required(true)
.and()
.attribute("firstname")
.type("http://axschema.org/namePerson/first")
.required(true)
.and()
.attribute("lastname")
.type("http://axschema.org/namePerson/last")
.required(true)
.and()
.and()
.attributeExchange(".*yahoo.com.*")
.attribute("email")
.type("http://axschema.org/contact/email")
.required(true)
.and()
.attribute("fullname")
.type("http://axschema.org/namePerson")
.required(true)
.and()
.and()
.attributeExchange(".*myopenid.com.*")
.attribute("email")
.type("http://schema.openid.net/contact/email")
.required(true)
.and()
.attribute("fullname")
.type("http://schema.openid.net/namePerson")
.required(true);
}
#Bean(name = "myAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
class CustomUserDetailsService implements AuthenticationUserDetailsService<OpenIDAuthenticationToken> {
#Override
public UserDetails loadUserDetails(OpenIDAuthenticationToken token) throws UsernameNotFoundException {
return new User(token.getName(), "", AuthorityUtils.createAuthorityList("ROLE_USER"));
}
}
}
The login page looks like this:
<form id="googleLoginForm" action="/j_spring_openid_security_check" method="post">
<h1>Login</h1>
<input name="openid_identifier" type="hidden" value="https://www.google.com/accounts/o8/id"/>
<input name="openid.ns.pape" type="hidden" value="http://specs.openid.net/extensions/pape/1.0"/>
<input name="openid.pape.max_auth_age" type="hidden" value="0"/>
<p>
<input name="submit" value="Login using Google" type="submit"/>
</p>
</form>
The problem is that the "/j_spring_openid_security_check" doesn't seem to exist. I think the problem is that I ought to extend from AbstractSecurityWebApplicationInitializer when using Spring Security but for boot I should use SpringBootServletInitializer. What's the best way to combine the two? The javadoc of SpringBootServletInitializer says that it registers a filter automatically when Spring Security is detected but it doesn't seem to work in this case.
I actually managed to solve this. First off all I used Spring Boot to start an embedded container so I didn't need any WebApplicationInitializers. Secondly the post URL in the login page should point to "/login/openid" and thirdly I had to disable cross-site request forgery prevention in the security configuration using:
http.csrf().disable(). ..
in the configure method in the SecurityConfig class.

Resources