Login page needs to be prompted if user is not authorized to access specific controller or URL in spring security. How to achieve that? - spring-boot

I'm using spring-boot, spring-security and JSP. If I click on a button it should go to a controller if user is logged in. Otherwise, it should first ask user to login and then get back to that page. In short, user should see the page if he is logged in. How can I achieve this?
I think filters/antmatchers might be used but I am wondering how the user will get back to that particular page/controller after logging in?

Try using something like this to allow users access to certain pages and then set the default success url accordingly. You can have a home page as I use here represented by "/" and once a user logs in they are redirected to your /welcome page.
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// Public access to login, landing, and error pages
http.authorizeRequests().antMatchers("/", "/login", "/errorpage").permitAll();
// Static resource permissions
http.authorizeRequests()
.antMatchers("/css/**", "/fonts/**", "/images/**", "/webfonts/**", "/js/**", "/webjars/**", "/messages/**")
.permitAll();
// Login specifications
http.formLogin().loginPage("/login").defaultSuccessUrl("/welcome", true);
// Logout specifications
http
.logout()
.deleteCookies("remove")
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.permitAll();
}
}

Inside WebSecurityConfigurerAdapter implementation, you need to inform a formLogin and specify the loginPage.
That's just enough to Spring to use the endpoint /login this way.
If you try to access a page without logged, for example /profile, you will be redirected to /login, and after logged, you'll be redirected to /profile
And in this example, you have 3 pages accessible without authentication / ,/homeand/info`
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home", "/info" ).permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
...
}

Related

Spring security makes a session when a user access the log in page

I'm making log in page, following official guide.
I want to add a function which makes redirect to home if a user join.
The logic is like this.
A user join with email, password and username.
Joinpage redirect to log in page.
if there is authentication, directly go to home.
How can I redirect to home with authentication?
#Slf4j
#Controller
#RequiredArgsConstructor
public class LoginController {
#GetMapping("/login")
public String loginForm(#ModelAttribute LoginForm loginForm) {
return "login/loginForm";
}
}
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserSecurityService userSecurityService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() // 인가 요청 받기
.antMatchers("/", "/home", "/join",
"/css/**", "/*.ico", "/error").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
.failureUrl("/login")
.usernameParameter("email")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.invalidateHttpSession(true);
}
basically, the security generates user session along the SessionCreationPolicy value when the user logins successfully.
if you want to change the session system to stateless system.
but following the below code.
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
and then, you need to set the authentication object.
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("principal", "credentials", null));

Simple Spring Security Authentication [duplicate]

Can't navigate from index.html to any other page, when clicking the button to navigate a blank login file is downloaded. I think this problem is linked to security file because at the beginning I didn't had it but after adding it many things have been broken.
This is the html code :
Log In
And this is security file :
#Configuration
#EnableWebSecurity
public class Security extends WebSecurityConfigurerAdapter{
// https://spring.io/guides/gs/securing-web/
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/","/register","/login","/css/**", "/js/**", "/images/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(11);
}
}
There is a login.html file in templates.
As soon as you override the default login page by specifying .loginPage(some_string), then the Spring Security default login configuration will be deactivated.
It does not matter what the value of some_string is, it is considered a custom login page even if the value is "/login".
In other words, with your current configuration, when you are overriding the default login page, Spring Security expects you to create the mapping for your custom login endpoint.
As Ratul Sharker said in the comment above, you need to add a #GetMapping("/login") that returns your custom login page.

How to make a custom UsernamePasswordAuthenticationFilter register at an endpoint other than /login?

I've been following a tutorial to implementing JWT authentication in Spring Boot but am trying to adapt it to a case where I have two WebSecurityConfigurerAdapter classes, one for my API (/api/** endpoints) and one for my web front-end (all other endpoints). In the tutorial, a JWTAuthenticationFilter is created as a subclass of UsernamePasswordAuthenticationFilter and added to the chain. According to the author, this filter will automatically register itself with the "/login" endpoint, but I want it to point somewhere different, such as "/api/login" because I'm using this authentication method for my API only.
Here's the security configuration code for both the API and front-end (with some abbrevation):
#EnableWebSecurity
public class MultipleSecurityConfigurations {
#Configuration
#Order(1)
public static class APISecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**")
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()));
}
}
#Configuration
public static class FrontEndSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/?logout")
.and()
.authorizeRequests()
.mvcMatchers("/").permitAll()
.mvcMatchers("/home").authenticated()
.anyRequest().denyAll()
;
}
}
}
The question is: how can I define an endpoint such as "/api/login" as the endpoint for my custom JWTAuthenticationFilter?
Or, do I need to change the filter to not be a subclass of UsernamePasswordAuthenticationFilter and if so, how would I configure that?
EDIT: Something I've tried:
I guessed that the /api/login endpoint needed to be .permitAll() and I tried using formLogin().loginProcessingUrl(), even though it's not really a form login - it's a JSON login. This doesn't work. When i POST to /api/login I end up getting redirected to the HTML login form as if I were not logged in. Moreover, my Spring boot app throws a weird exception:
org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL contained a potentially malicious String ";"
The configuration I'm trying now:
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**")
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.formLogin().loginProcessingUrl("/api/login").and()
.authorizeRequests()
.antMatchers("/api/login").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()));
}
Since JWTAuthenticationFilter is a UsernamePasswordAuthenticationFilter, you could change the login endpoint directly on the filter instance:
JWTAuthenticationFilter customFilter = new JWTAuthenticationFilter(authenticationManager());
customFilter.setFilterProcessesUrl("/api/login");
http.addFilter(customFilter);
This configures JWTAuthenticationFilter to attempt to authenticate POST requests to /api/login.
If you wish also to change the default POST to another method (e.g. GET), you can set the RequiresAuthenticationRequestMatcher instead. For instance:
customFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/login", "GET"));

Spring Security - Authentication issue

I am working on a web application & have opted to use spring Security. The idea is for the user to be authenticated to see the Home Page, if the user is not authenticated they are redirected to the login page. This login page also displays a link to a registration form, This part is working correctly.
However, I have encountered an issue when attempting to allow users to sign up via the registration link. The link to the registration form cannot be accessed if the user if not authenticated ("showRegistrationForm")
Can anyone provide insight to why this is occuring? I have Included the code snippet from my SecurityConfig below
#Override
protected void configure(HttpSecurity http) throws Exception {
//Restrict Access based on the Intercepted Servlet Request
http.authorizeRequests()
.antMatchers("/resources/**", "/register").permitAll()
.anyRequest().authenticated()
.antMatchers("/").hasRole("EMPLOYEE")
.antMatchers("/showForm/**").hasAnyRole("EMPLOYEE","MANAGER", "ADMIN")
.antMatchers("/save/**").hasAnyRole("MANAGER", "ADMIN")
.antMatchers("/delete/**").hasRole("ADMIN")
.and()
.formLogin()
// Show the custom form created for the below request mappings
.loginPage("/showSonyaLoginPage")
.loginProcessingUrl("/authenticateTheUser")
// No need to be logged in to see the login page
.permitAll()
.and()
// No need to be logged in to see the logout button.
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/access-denied");
}
Change the code like below:
#Override
protected void configure(HttpSecurity http) throws Exception {
// Restrict Access based on the Intercepted Servlet Request
http.authorizeRequests()
.antMatchers("/showRegistrationForm/").permitAll()
.anyRequest().authenticated()
.antMatchers("/").hasRole("EMPLOYEE")
.antMatchers("/resources/").permitAll()
.antMatchers("/showForm/**").hasAnyRole("EMPLOYEE","MANAGER", "ADMIN")
.antMatchers("/save/**").hasAnyRole("MANAGER", "ADMIN")
.antMatchers("/delete/**").hasRole("ADMIN")
.and()
.formLogin()
// Show the custom form created for the below request mappings
.loginPage("/showSonyaLoginPage")
.loginProcessingUrl("/authenticateTheUser")
// No need to be logged in to see the login page
.permitAll()
.and()
// No need to be logged in to see the logout button.
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/access-denied");
}
Moved down the below code:
anyRequest().authenticated()

Spring Boot Security - Multiple configurations

I'm working (and struggling a little bit) on an example using spring-boot with spring security.
My system is using a web app and also provide an REST-API, so i would like to have form based security (web) and basic auth (resp api).
As the spring documentation recommend (https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#multiple-httpsecurity), I need to create a multi http web security configuration.
The main code works, but if I use Postman for the test of my RestApi following use-case does not work.
All GET-requests to /restapi/ working without authentication (statuscode 200)
All POST-requests to /restapi/ without the BASIC Auth Header are working (statuscode 401)
All POST-requests to /restapi/ with a correct BASIC Auth Header are work (statuscode 200)
BUT all requests with a wrong BASIC Auth header (f.e. user1/1234567) are returning the HTML-Loginpage defined in the first WebSecurityConfigurerAdapter (FormWebSecurityConfigurerAdapter)
Does anyone has an idea - what is wrong with my configuration?
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Autowired
private static RestAuthenticationAccessDeniedHandler restAccessDeniedHandler;
#Autowired
public void configureAuth(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication()
.withUser("admin").password("{noop}12345678").roles("ADMIN").and()
.withUser("user").password("{noop}12345678").roles("USER");
}
#Configuration
#Order(1)
public static class RestWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/restapi/**")
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/restapi/**").permitAll()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(UNAUTHORIZED))
.and()
.exceptionHandling().accessDeniedHandler(restAccessDeniedHandler) ;
}
}
/*
Ensures that any request to our application requires the user to be authenticated (execpt home page)
Requests matched against "/css/**", "/img/**", "/js/**", "/index.html", "/" are fully accessible
Allows users to authenticate with HTTP Form Based authentication
Configure logout with redirect to homepage
*/
#Configuration
public static class FormWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**", "/img/**", "/js/**", "/index.html", "/").permitAll()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/index.html")
.permitAll();
}
}
}
I know it is a question from some time ago but I still want to share the answer for people who are struggling with this issue.
After a lot of searching I found out that the /error endpoint in spring boot 2.x is now secured by default. What I mean to say is in the past the /error was a endpoint what had no security at all (or didn't exist). The solution to this issue is quite straight forward.
antMatchers('/error').permitAll()
within your web security adapter configuration(s).
What happens if you don't do this, the security will check the endpoint against your configuration and if it cannot find this endpoint (/error) it will redirect to the standard login form, hence the 302.

Resources