I follow spring security 3.2 doc to write a sample app. http.authorizeRequests().anyRequest().authenticated() is this mean any request is deny who is not login? But i access any url it's accessable. Is something config i has missing?
#Configuration
public class SpringWebMVCApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { SecurityConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
springmvc config
#Configuration
#EnableWebMvc
#ComponentScan("org.jxs.mm.controller")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/assets/**").addResourceLocations("/assets/");
registry.addResourceHandler("/favicon.ico").addResourceLocations("/favicon.ico");
}
}
spring security config
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated();
}
}
You probably haven't registered your springSecurityFilterChain with the war. See section 3.1.1 in Spring Security documentation
To summarize:
SecurityConfig class defines your Spring Security configuration. It configures the springSecurityFilterChain filter.
However, this filter chain needs to be applied to/registered with/associated with all URLs in your application (so that the URLs get intercepted by the springSecurityFilterChain). This can be done by extending AbstractSecurityWebApplicationInitializer like so:
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}
After this, Spring Security should intercept any URL and apply the appropriate security rules as configured.
You can grant access to a particular RESTFul Url that require no authentication with keyword "permitAll" and "hasAnyAuthority" for pages that do.
http
.formLogin()
.loginPage("/signin")
.loginProcessingUrl("/signin/authenticate")
.failureUrl("/loginfail")
// Grant all access to login url
.permitAll()
.and()
.logout()
.logoutUrl("/signout")
.logoutSuccessUrl("/signin")
.and()
.authorizeRequests()
.antMatchers("/foo/**").permitAll() //No authentication required
.antMatchers("/").hasAnyAuthority("ROLE_USER","ROLE_ADMIN") //Authentication required (access granted to users with role "ROLE_USER" or "ROLE_ADMIN")
}
Related
I have some API, several resources should be available to everyone, the rest for users.
I to proctect resources I have implemented a class which extends WebSecurityConfigurerAdapter like here:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(final HttpSecurity http) throws Exception
{
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2ResourceServer()
.authenticationManagerResolver((request) -> http.getSharedObject(AuthenticationManager.class))
.and().oauth2Login()
.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and().cors();
}
}
And then I was trying to follow https://www.baeldung.com/spring-deny-access to allow some of resources be accessible to everyone
So I did according to this example
GlobalMethodSecurityConfiguration & WebSecurityConfigurerAdapter
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration
{
#Override
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
return new CustomPermissionAllowedMethodSecurityMetadataSource();
}
#Configuration
public static class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2ResourceServer()
.authenticationManagerResolver((request) -> http.getSharedObject(AuthenticationManager.class))
.and().oauth2Login()
.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and().cors();
}
}
}
and the implementation of CustomPermissionAllowedMethodSecurityMetadataSource
public class CustomPermissionAllowedMethodSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource
{
#Override
protected Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass)
{
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
List attributes = new ArrayList<>();
// if the class is annotated as #Controller we should by default deny access to all methods
if (AnnotationUtils.findAnnotation(targetClass, Controller.class) != null)
{
attributes.add(DENY_ALL_ATTRIBUTE);
}
if (annotations != null)
{
for (Annotation a : annotations)
{
// but not if the method has at least a PreAuthorize or PostAuthorize annotation
if (a instanceof PreAuthorize || a instanceof PostAuthorize)
{
return null;
}
}
}
return attributes;
}
#Override
protected Collection<ConfigAttribute> findAttributes(Class<?> clazz)
{
return null;
}
#Override
public Collection<ConfigAttribute> getAllConfigAttributes()
{
return null;
}
}
At the end I have added to the endpoint in rest controller:
#PreAuthorize("permitAll()")
Unfortunately, without a user, I cannot access this endpoint.
Do I use GlobalMethodSecurityConfiguration and a WebSecurityConfigurerAdapter wrong?
Is it a correct way to achieve what I mentioned at the beginning (some endpoints protected, some not)?
You have to understand the difference between Method Security and Http Security.
Method security is how to protect methods internally from being called. This is usually used in for instance client applications, desktop applications etc. Here you place an annotation on a specific method and will protect it from being called internally.
HttpSecurity is an implementation that deals with how to protect http api endpoints. This is usually done with preimplemented filters in spring boot and this is what you should be looking at, not method security.
You have currently implemented method security and trying to protect http endpoints using it.
I suggest you start with the spring security hello world java configuration part in the official documentation to learn how to implement HttpSecurity in spring boot. Or here is another tutorial.
And here is an example
#Configuration
#EnableWebSecurity
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1").password(passwordEncoder().encode("user1Pass"))
.authorities("ROLE_USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/securityNone").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
this configuration permits all requests to /securityNone and sets all other endpoints to need authentication.
I am setting up an app with 2 differents users:
one from my ldap that can connect with cas authentication
one external, hard coded with a simple formlogin
I created 2 Security configurations:
External User Configuration:
#EnableWebSecurity
#Configuration
#RequiredArgsConstructor
#Order(1)
public class SecurityVanuatuConfiguration extends WebSecurityConfigurerAdapter {
#Bean
#Override
public UserDetailsService userDetailsService() {
UserDetails user =
User.withUsername("user")
.password("{noop}user")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable().antMatcher("/user/**")
.authorizeRequests()
.requestMatchers(SecurityUtils::isFrameworkInternalRequest).permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage(LOGIN_URL).permitAll()
.loginProcessingUrl(LOGIN_PROCESSING_URL)
.failureUrl(LOGIN_FAILURE_URL)
.successHandler(successHandler)
.failureHandler(successHandler)
.and().logout().logoutSuccessUrl(LOGOUT_SUCCESS_URL);
}
Cas Configuration:
#EnableWebSecurity
#Configuration
#RequiredArgsConstructor
#Order(2)
public class SecurityCasConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().antMatcher("/admin/**")
.authorizeRequests()
.requestMatchers(SecurityUtils::isFrameworkInternalRequest).permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint())
.and()
.addFilter(casAuthenticationFilter())
.addFilterBefore(casLogoutFilter(), CasAuthenticationFilter.class);
}
#Bean
public SingleSignOutFilter casLogoutFilter() {
SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
return singleSignOutFilter;
}
// if I remove this bean, external configuration works
#Bean
public CasAuthenticationProvider casAuthenticationProvider() {
CasAuthenticationProvider provider = new CasAuthenticationProvider();
provider.setServiceProperties(serviceProperties());
provider.setTicketValidator(new Cas30ServiceTicketValidator("https://sso.unc.nc/cas"));
provider.setKey("cas");
provider.setAuthenticationUserDetailsService(successHandler);
return provider;
}
Each configuration work when it's alone, but when there is both, the external doesn't work.
It seems that the CasAuthenticationProvider bean prevent the formLogin to work and I endup in the FailureHandler.
Here is the error:
org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken
How can I make these 2 configuration work together?
when you mark something as #Bean you are putting it in the pool of beans that spring can use to inject into classes if they need it.
you have registered
#Bean
public CasAuthenticationProvider casAuthenticationProvider() {
...
}
as a #Bean which is an AuthenticationProvider. When you register this using #Bean it will get picked up by everyone that needs it, meaning that both your WebSecurityConfigurerAdapter will use it.
Spring has no way of knowing that you only want this is in one of your security configurations and not the other.
you have to explicitly define that you want it in one and not the other by removing #Bean and setting it manually.
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(casAuthenticationProvider());
}
public CasAuthenticationProvider casAuthenticationProvider() {
...
}
You have to always decied if you want to set something manually, or use #Bean and let spring inject it for you.
So for instance, you are registering a filter (casLogoutFilter) setting it manually but also defining it as #Bean telling spring to inject it into the filter chain, which sort of doesn't make sense.
I am new to spring security and I was following this example on configuring spring security : https://spring.io/blog/2013/07/03/spring-security-java-config-preview-web-security/. So I saw that they use this method to let the spring know for the configuration.
public class SpringWebMvcInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { HelloWebSecurityConfiguration.class };
}
...
}
But I have app initialization like this:
public class AppInit implements WebApplicationInitializer{
public void onStartup(ServletContext servletContext) throws ServletException {
// TODO Auto-generated method stub
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AppConfiguration.class);
ServletRegistration.Dynamic registration =
servletContext.addServlet("dispatcher", new DispatcherServlet(context));
registration.setLoadOnStartup(1);
registration.addMapping("/services/rest/*");
}
}
And I want to include my spring security configuration there, as without it I get message in browser: Your login attempt was not successful, try again.
Reason: No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken
Extend from AbstractAnnotationConfigDispatcherServletInitializer is a way to make spring to load the security config, but I don't use it. A more convinient way to accomplish this can be like this(decalare the dependency of spring security in pom.xml first):
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication().withUser("user").password("user").roles("USER")
.and().withUser("admin").password("admin").roles("USER","ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/").hasRole("USER")
.antMatchers("/index").hasRole("USER")
.antMatchers("/message/*").hasRole("USER")
.anyRequest().permitAll()
.and().formLogin().loginPage("/login").defaultSuccessUrl("/index").failureUrl("/login?error").permitAll()
.and().rememberMe().tokenValiditySeconds(60*60*7).key("message")
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll();
// define your action here.
}
}
Spring will load this config automatically on startup for you, this is enough for spring security to work. As you see, you should define the rules in configure(HttpSecurity http) to tell spring security what to do when a request is coming.
You can just register your security config in your AppInit class by changing the line
context.register(AppConfiguration.class);
to
context.register({HelloWebSecurityConfiguration.class, AppConfiguration.class});
I have working REST API under Spring 4 using Basic authentication. These REST services are under /api/v1/** URL. However, I want to add another set of REST endpoints under different url /api/v2/**, but protected with token-based authentication.
Is it possible to do this with one servlet ? How to configure Spring Security to use different forms of authentication for different URLs ?
Thank you.
Here's a code sample in Java config that uses UserDetailsService and has different security configurations for different URL endpoints:
#Configuration
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/v1/**")
.httpBasic()
.realmName("API")
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/v1/**").authenticated();
}
}
#Configuration
#Order(2)
public static class ApiTokenSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/v2/**")
/* other config options go here... */
}
}
}
I created a Spring Security configuration class for Spring-Boot. My login page has resources css, js and ico files. The resources are getting denied for security reasons and redirected to login page each time. Why does EnableWebMVCSecurity not add the Classpath resource location. After changing the code as in the second snippet the I Classpath resource location is added. dont understand what I am missing for the resources in the first code snippet.
#Configuration
/*
* Enable Spring Security’s web security support and provide the Spring MVC integration
* It also extends WebSecurityConfigurerAdapter and overrides a couple of its methods to set some specifics of the web security configuration.
*/
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* The configure(HttpSecurity) method defines with URL paths should be
* secured and which should not.
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated();
// There is a custom "/login" page specified by loginPage(), and everyone
// is allowed to view it.
http
.formLogin()
.loginPage("/login.html")
.permitAll()
.and()
.logout()
.permitAll().logoutSuccessUrl("/login.html");
}
#Configuration
protected static class AuthenticationConfiguration extends
GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
// As for the configure(AuthenticationManagerBuilder) method, it sets up
// an in-memory user store with a single user. That user is given a
// username of "user", a password of "password", and a role of "USER".
auth
.inMemoryAuthentication()
.withUser("user#domain.com").password("password").roles("USER");
}
}
I got this working by changing the code to
#Configuration
/*
* Enable Spring Security’s web security support and provide the Spring MVC integration
* It also extends WebSecurityConfigurerAdapter and overrides a couple of its methods to set some specifics of the web security configuration.
*/
public class WebSecurityConfig{
#Bean
public ApplicationSecurity applicationSecurity() {
return new ApplicationSecurity();
}
#Bean
public AuthenticationSecurity authenticationSecurity() {
return new AuthenticationSecurity();
}
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated();
http
.formLogin()
.loginPage("/login.html")
.permitAll()
.and()
.logout()
.permitAll().logoutSuccessUrl("/login.html");
}
}
#Order(Ordered.HIGHEST_PRECEDENCE + 10)
protected static class AuthenticationSecurity extends
GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user#domain.com").password("password").roles("USER");
}
}
}
After changing the code I noticed that the Ignore paths were added to the filter and I see the following in logs:
[ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: Ant [pattern='/css/**'], []
[ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: Ant [pattern='/js/**'], []
[ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: Ant [pattern='/images/**'], []
[ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: Ant [pattern='/**/favicon.ico'], []
[ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher#1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#4e3e0069, org.springframework.security.web.context.SecurityContextPersistenceFilter#3d2dd0cf, org.springframework.security.web.header.HeaderWriterFilter#33fc3b02, org.springframework.security.web.csrf.CsrfFilter#9b7a3ac, org.springframework.security.web.authentication.logout.LogoutFilter#267237ef, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#129495ef, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter#7db0a467, org.springframework.security.web.authentication.www.BasicAuthenticationFilter#764d1dbd, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#25a5268d, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#15c01d0c, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#37818a3b, org.springframework.security.web.session.SessionManagementFilter#3fe57e49, org.springframework.security.web.access.ExceptionTranslationFilter#4278af59, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#424bef91]
Per the docs you have disabled the spring boot autoconfig in the first example by using #EnableWebSecurity, so you would have to explicitly ignore all the static resources manually. In the second example you simply provide a WebSecurityConfigurer which is additive on top of the default autoconfig.
Create a Configuration file that extends WebSecurityConfigurerAdapter and annotate the class with #EnableWebSecurity
You can override methods like configure(HttpSecurity http) to add basic security like below
#Configuration
#EnableWebSecurity
public class AppWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().permitAll();
}
}
Add below method to by pass security for css and js in security config -
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/** **","/js/** **");
}