Spring Security blocking Rest Controller - spring

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>

Related

Can't logout by custom logout handler

In my spring boot app I want to add link to logout.
In template welcome.html:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title th:text="${appName}">Template title</title>
<body>
<h1>Welcome to <span th:text="${appName}">Our App</span></h1>
<h2>Dictionaries:</h2>
<p>Categories</p>
<p>Users</p>
<p>Logout</p>
</body>
</html>
In my SecurityConfiguration
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;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.web.DefaultRedirectStrategy;
import ru.otus.software_architect.eshop.handler.CustomLogoutSuccessHandler;
import javax.sql.DataSource;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource; // get by Spring
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// Here, you are making the public directory on the classpath root available without authentication (e..g. for css files)
.antMatchers("/public/**", "/registration.html").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")
.successHandler((request, response, authentication) -> new DefaultRedirectStrategy().sendRedirect(request, response, "/welcome"))
.failureUrl("/login-error.html")
.permitAll()
.and()
.logout()
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
.permitAll();
}
// login by user from db
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.usersByUsernameQuery("SELECT username, password, active FROM usr WHERE username=?")
.authoritiesByUsernameQuery("SELECT u.username, ur.role FROM usr u INNER JOIN user_roles ur ON u.id = ur.user_id WHERE u.username=?");
}
My handler:
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
private static Logger logger = LogManager.getLogger(CustomLogoutSuccessHandler.class);
#Override
public void onLogoutSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
logger.info("onLogoutSuccess:");
// some custom logic here
response.sendRedirect(request.getContextPath() + "/login.html");
}
}
as you can see it just forward to login page.
But when I click on welcome.html on logout link the method onLogoutSuccess is not call and I get error page:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sun Jan 26 18:21:53 EET 2020
There was an unexpected error (type=Not Found, status=404).
No message available
Logout will redirect you to "/logout" performing a GET request.
Unless you have a page rendered at "/logout", you will receive a 404.
By default logout is only triggered with a POST request to "/logout".
Since you are using Thymeleaf, you could instead use something like this for logging out.
<form action="#" th:action="#{/logout}" method="post">
<input type="submit" value="Logout" />
</form>

HTTPS Secured works only on home page not the next page

I am deploying a spring boot webapplication in AWS EC2 instance on port 80 and the home page is displayed as Secured When I click on the link like user login or admin login the browser shows it as Not Secured.What should I do to make my whole application secured.
Below is my code which I am using from a site,I am new to spring security,Please help.
Home.html
<div class="starter-template">
<h1>Spring Boot Web Thymeleaf + Spring Security</h1>
<h2>1. Visit <a th:href="#{/admin}">Admin page (Spring Security protected, Need Admin Role)</a></h2>
<h2>2. Visit <a th:href="#{/user}">User page (Spring Security protected, Need User Role)</a></h2>
<h2>3. Visit <a th:href="#{/about}">Normal page</a></h2>
</div>
#Configuration
// http://docs.spring.io/spring-boot/docs/current/reference/html/howto-security.html
// Switch off the Spring Boot security configuration
//#EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AccessDeniedHandler accessDeniedHandler;
// roles admin allow to access /admin/**
// roles user allow to access /user/**
// custom 403 access denied handler
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/home", "/about").permitAll()
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.antMatchers("/user/**").hasAnyRole("USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
.and()
.withUser("admin").password("password").roles("ADMIN");
}
}
What am I doing wrong?Is the issue in the code or my AWS Configuration
Solved the issue by setting application.properties to the below
server.use-forward-headers=true

Spring Security 5 authentication always return 302

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

Return to the previous page after authorization, Spring Security AuthenticationSuccessHundler

I have a login page (/page/login) and dropdown login forms in every page. I want user to be redirected to the page from which he has logged in (by dropdown login form), or the home page if it was from login page.
I tried to use AuthenticationSuccessHandler but it does not seems to work, every time it just redirects user to home page. What is the right way to solve it?
#Component
public class MySimpleUrlAuthenticationSuccessHendler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Authentication authentication) throws IOException {
if(httpServletRequest.getContextPath().equals("/login")){
sendRedirect(httpServletRequest, httpServletResponse, "/user/profile");
}
else{
sendRedirect(httpServletRequest, httpServletResponse,httpServletRequest.getContextPath());
}
}
private void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException {
if(!response.isCommitted()){
new DefaultRedirectStrategy().sendRedirect(request,response,url);
}
}
}
Spring security config
package com.example.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
#Configuration
public class DemoSpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
AuthenticationSuccessHandler authenticationSuccessHandler;
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.regexMatchers(HttpMethod.GET, "rating/place/[0-9]{0,}", "/place/[0-9]{0,}/liked/", "/rating/place/[0-9]{0,}")
.hasRole("USER")
.antMatchers(HttpMethod.GET, "/user/orders",
"/user/places")
.hasRole("USER")
.regexMatchers(HttpMethod.POST, "/menu/[0-9]{0,}/comment",
"/place/[0-9]{0,}/menu/[0-9]{0,}")
.hasRole("USER")
.regexMatchers(HttpMethod.POST, "/place/menu/[0-9]{0,}")
.hasRole("OWNER")
.antMatchers(HttpMethod.GET, "/newplace")
.authenticated()
.antMatchers(HttpMethod.POST, "/newplace")
.authenticated()
.antMatchers(HttpMethod.POST, "/registration")
.permitAll()
.antMatchers(HttpMethod.GET, "/resend", "/page/login", "/registration", "/place/")
.permitAll();
http
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/");
http
.rememberMe()
.key("rememberme");
http
.formLogin()
.loginPage("/page/login")
.failureUrl("/page/login")
.loginProcessingUrl("/login")
.usernameParameter("j_username")
.passwordParameter("j_password")
.successHandler(authenticationSuccessHandler);
http.
userDetailsService(userDetailsService);
http.
csrf().disable();
}
}
You need something like this in your AuthenticationSuccessHandler.
I also had similar requirement in my project and I solved this using below step:-
When the login form in dropdown is submitted I also send the current url (window.location.href) as a hidden request parameter.
Inside UserNamePasswordFilter and I get this parameter from request and store it in session (say variable name is redirectPrevUrl).
Now, in authentication success handler if this variable is present (i.e. redirectPrevUrl!=null) I redirect to this url instead of default home page.
This worked for me and I hope it will work for you as well,

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.

Resources