Spring Boot #PreAuthorize at class level - spring-boot

I have a Spring Boot controller. My controller is derived from a base class. If I annotate a method in the base class or the base class itself with:
#RequestMapping(value = "/page", method = RequestMethod.POST)
#PreAuthorize("hasRole('BACKOFFICE')")
public ResponseEntity<?> page(#RequestBody ObjectNode json, Pageable pageable, Authentication authentication) throws ApplicationException{
...
}
#RestController
#PreAuthorize("hasRole('BACKOFFICE')")
public abstract class TableController {
...
}
it works and roles are checked. I need to annotate the derived class but there #PreAuthorized annotation is ignored.
#RestController
#PreAuthorize("hasRole('BACKOFFICE')")
public class UtentiController extends TableController{
...
}
Is there a different annotation or a cofinguration required?
My security configuration is
#Configuration
#EnableWebSecurity
#EnableTransactionManagement
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailServiceImpl userService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(userService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers( "/css/**", "/libs/**","/js/**", "/api/**", "/assets/**", "/register", "/public/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/login?logout")
.permitAll()
.and()
.csrf().disable();
}
}

Related

How to secure #PostMapping endpoint by spring security without secure #GetMapping

I would like to add Spring Security to my app on few endpoints.
#RestController
#RequestMapping("/test")
#RequiredArgsConstructor
public class TestController {
#PostMapping
public ResponseEntity post() {
...
}
#GetMapping
public ResponseEntity get() {
...
}
In web security conifure adapter I know how to secure endpoit. I did:
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
private final MyUserDetailsService myUserDetailsService;
#Override
public void configure(HttpSecurity http) throws Exception {
http.httpBasic().and()
.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/users", "/users/**").permitAll()
.anyRequest().authenticated()
;
}
In endpoint "/test" I would like to add security in #PostMapping where only authenticated users will be able to post something. #GetMapping will be open for everyone.
EDIT
So I updated my MyWebSecurityConfig:
#Override
public void configure(HttpSecurity http) throws Exception {
http.httpBasic().and()
.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/users", "/users/**").permitAll()
.antMatchers(HttpMethod.GET, "/shapes").permitAll()
.antMatchers(HttpMethod.GET, "/shapes/history").permitAll()
.anyRequest().authenticated()
;
}
but I can't still send Get on "/shapes" endpoit without authentication.
I am still getting 401 Unauthorized. What should I change?
There is an overrided version of antMatchers() that allow you to configure matching a HTTP method and the path together :
.authorizeRequests()
.antMatchers("/users", "/users/**").permitAll()
.antMatchers(HttpMethod.GET,"/test").permitAll()
.antMatchers(HttpMethod.POST, "/test").authenticated()

configure(HttpSecurity http) and configure(AuthenticationManagerBuilder) get ignored with certain annotations in WebSecurityConfigurerAdapter

I'm trying to secure my spring application that has different user roles. While the Authentication part is set and works flawlessly, I realised during the implementation of the Authorisation part that with certain annotations, one of the two overrides methods inside my SecurityConfiguration extends WebSecurityConfigurerAdapter class, gets ignored.
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private WebApplicationContext applicationContext;
private CredentialsService userDetailsService;
#Autowired
private DataSource dataSource;
#PostConstruct
public void completeSetup() {
userDetailsService = applicationContext.getBean(CredentialsService.class);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login")
.permitAll()
.and()
.formLogin()
.permitAll()
.and()
.httpBasic()
.disable()
.authorizeRequests()
.antMatchers("/admin", "/admin/**")
.hasRole("ADMIN")
.and()
.authorizeRequests()
.antMatchers("/employee", "/employee/**")
.hasRole("EMPLOYEE")
.and()
.authorizeRequests()
.antMatchers("/customer", "/customer/**")
.hasRole("CUSTOMER");
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(encoder())
.and()
.authenticationProvider(authenticationProvider())
.jdbcAuthentication()
.dataSource(dataSource);
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(12);
}
}
Now the problem is the following, as it is, this class authenticate my users but has one major drawback: the
configure(final HttpSecurity http) throws Exception {
gets completely ignored.
On the other side though, if I add the #Configuration annotation on top of my class, the
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
gets completely ignored, hence will break the authorisation as it won't be able to call the getUsername() and getPassword on my custom UserDetailsService implementation.
As you can see, I've used a DaoAuthenticationProvider instance as authenticationProvider, since my application retrieve the users/password from an external database.
The quick fix I adopted right now it's the addition of the following method on my main class
#EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
and the use of the #Secured annotation on my restricted controllers. That works, but I'd like to understand why Spring has such strange behaviour and what step can I take to address these problems.
Since you are assigning roles to your users, use the syntax
.antMatchers("/admin", "/admin/**")
.hasRole("ADMIN")
OR
.antMatchers("/admin", "/admin/**")
.hasAuthority("ROLE_ADMIN")
Roles are just stored as authorities with the "ROLE_" prefix.
So the role "ADMIN" is equivalent to the authority "ROLE_ADMIN".
EDIT 1
You can also simplify your configuration to make it clear where everything is coming from.
Since you UserDetailsService (CredentialsService) is already a bean, it will be picked up automatically by Spring Security.
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
// The password encoder should be a bean
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(12);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login")
.permitAll()
.and()
.formLogin()
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/admin", "/admin/**")
.hasRole("ADMIN")
.and()
.authorizeRequests()
.antMatchers("/manager", "/manager/**")
.hasRole("MANAGER")
.and()
.authorizeRequests()
.antMatchers("/customer", "/customer/**")
.hasRole("CUSTOMER");
}
}

How do I implement HttpSessionBindingListener? (Spring Security)

I am using Spring MVC without Spring Boot and without web.xml
I need to implement HttpSessionBindingListener.
I don't know how to get this to work.
I've tried many options but this doesn't want to work.
https://github.com/romanych2021/TestBindingListener
UserActive.java
#Service
public class UserActive implements HttpSessionBindingListener {
Logger logger = LoggerFactory.getLogger(UserActive.class);
#Override
public void valueBound(HttpSessionBindingEvent event) {
logger.info("Log in : {}", event.getName() );
}
#Override
public void valueUnbound(HttpSessionBindingEvent event) {
logger.info("Log out : {}", event.getName() );
}
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.mvcMatchers("/").permitAll()
.mvcMatchers("/login").anonymous()
.mvcMatchers("/user").hasAnyRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/")
.and().csrf().disable()
.logout()
.permitAll()
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID");
}
}
RootConfig.java
#EnableWebMvc
#Configuration
#EnableWebSecurity
#ComponentScan({"com.testbindinglistener.security", "com.testbindinglistener.service"})
public class RootConfig {
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}

multiple entry points in spring security

I have a spring boot application that should allow form based authentication against database and SSO CAS based authentication.
I have followed the example from here (https://www.baeldung.com/spring-security-multiple-entry-points) and seems to me that Order is not working as expected. it is always using the one that is annotated as Order(1) as entry point.
here is my code,
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Configuration
#Order(2)
public static class WebSecurityCASConfig extends WebSecurityConfigurerAdapter {
public WebSecurityCASConfig() {
super();
}
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(
"/js/**",
"/css/**",
"/images/**").permitAll()
.regexMatchers("/login1")
.authenticated()
.and()
.authorizeRequests()
.and()
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint);
}
}
//second
#Configuration
#Order(1)
public static class WebSecurityDatabaseConfig extends WebSecurityConfigurerAdapter {
public WebSecurityDatabaseConfig() {
super();
}
#Autowired
UserDetailServiceImpl userDetailsService;
#Autowired
BCryptPasswordEncoder passwordEncoder;
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(
"/js/**",
"/css/**",
"/images/**").permitAll()
//.antMatchers("/catalog").access("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
////.antMatchers("/login1").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/catalog", true)
.permitAll()
.usernameParameter("username")
.passwordParameter("password")
.and()
.logout()
.permitAll()
.logoutUrl("/logout").logoutSuccessUrl("/logout")
.and().exceptionHandling().accessDeniedPage("/403");
}
}
}
I want both configurations work based on url pattern. Any solutions/help/suggestions would be highly appreciated. Thanks.
I found a solution for this. I just simply followed what the spring document says in 5.9 (https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/reference/htmlsingle/) and also another question on stackoverflow, Spring Security : Multiple HTTP Config not working

how to define the secured URLs within an application dynamically

I am using Spring framework version: 3.2.3.RELEASE and
Spring security version: 3.2.3.RELEASE
I want to define the secured URLs within an application dynamically,and I have tried for several ways . I just can't make it,please help me!
According to http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/faq.html#faq-dynamic-url-metadata ,
I learn that dynamically defining access control to URLs needs using an explicitly declared security filter chain in order to customize the FilterSecurityInterceptor bean.
My question is
how to explicitly declared security filter chain?are there any example?
according to the book Spring security 3.1
which said"We can use a custom BeanPostProcessor to replace the standard FilterInvocationServiceSecurityMetadataSource with our custom implementation."
I followed this way and it dosen't work, I wondering if I have done something wrong?
the Java config class looks like this:
public class MessageSecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
#Configuration
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
#Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
}
#Component
public class KpiFilterInvocationSecurityMetadataSourceBeanPostProcessor implements BeanPostProcessor {
#Autowired
private KpiFilterInvocationSecurityMetadataSource metadataSource;
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof FilterInvocationSecurityMetadataSource) {
return metadataSource;
}
if(bean instanceof FilterChainProxy.FilterChainValidator) {
return new FilterChainProxy.FilterChainValidator() {
#Override
public void validate(FilterChainProxy filterChainProxy) {
}
};
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
I found a method withObjectPostProcessor,and try to do the following,it still doesn't worked. so what is best way to leverage this method?
#Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private KpiFilterInvocationSecurityMetadataSource metadataSource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.authorizeRequests()
.antMatchers("/signup/**", "/about", "/login/**", "/index")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll()
.loginPage("/login/form")
.loginProcessingUrl("/login")
.usernameParameter("policeNo")
.passwordParameter("password")
.failureUrl("/login/form?error")
.defaultSuccessUrl("/default")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login/form?logout")
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.withObjectPostProcessor(
new ObjectPostProcessor<FilterSecurityInterceptor>() {
public <O extends FilterSecurityInterceptor> O postProcess(
O fsi) {
fsi.setSecurityMetadataSource(metadataSource);
return fsi;
}
});
}
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers("/resources/**");
}
}

Resources