I want to add spring security(using Java config) into my application with custom authentication provider, however I cannot make it work. It seems that the authenticationProvider was not well configured, coz I cannot debug into the method authenticate(Authentication), and the println doesn't print anything. And every request responsed with 403.
Please anyone can help me with this, I've been struck by this for the whoele weekend.
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new AuthenticationProvider(){
#Override
public Authentication authenticate(Authentication arg0) throws AuthenticationException {
System.out.println("authenticating...");
return arg0;
}
#Override
public boolean supports(Class<?> arg0) {
return true;
}
});
}
}
The authenticate() function of the class must return an UsernamePasswordAuthenticationToken instance if the authentication is successful or null otherwise.
**
The org.springframework.security.authentication.ProviderManager and configure is (set its providers) to a custom
org.springframework.security.authentication.AuthenticationProvider;
This should return on its authenticate method a Authentication, which
should be setted with the GrantedAuthority
**
Here is a sample authentication manager code that sets authorities and returns an authentication object.
Can you try injecting the customAUthenticationProvider class you have and then use that in the configure method?
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
Are the web xml mappings in place for spring security?
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- use the springSecurityFilterChain -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter- class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!-- NOTE This does not specify its own configuration it is
loaded by the ContextLoaderListener instead -->
<!-- NOTE by default the filter name is used to
look up the Spring Security Filter Chain if you like you
can use any filter name you want, but you must specify
the bean name instead in this instance. Since we use the
springSecurityFilterChain as the filter name this is not
necessary
<init-param>
<param-name>targetBeanName</param-name>
<param-value>springSecurityFilterChain</param-value>
</init-param> -->
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Here is a sample spring security project : https://github.com/spring-projects/spring-security-javaconfig
Related
I'm updating an internal library and applications to Spring Boot 3 (which updated to Spring Security 6).
We have common security configuration which I've updated to implement AbstractHttpConfigurer e.g.
public class WebberWebSecurityConfigurerAdapter
extends AbstractHttpConfigurer<WebberWebSecurityConfigurerAdapter, HttpSecurity> {
public void configure(final HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.requestMatchers(HEALTH_CHECK_PATH).permitAll()
}
}
The web application then has:
#Configuration
public class SecurityConfig {
#Bean
public SecurityFilterChain config(HttpSecurity http) throws Exception {
http.apply(new WebberWebSecurityConfigurerAdapter());
http
.authorizeHttpRequests()
.requestMatchers("/", "/request-info", "/test").permitAll()
.anyRequest().authenticated();
return http.build();
}
}
This throws the exception:
Caused by: java.lang.IllegalStateException: Can't configure requestMatchers after anyRequest
Presumably because Spring is trying to configure the .requestMatchers(HEALTH_CHECK_PATH).permitAll() line after the .anyRequest().authenticated(); line, despite it being specified first in the config.
How do I get Spring to configure all the upstream library config first, before applying the custom config from the web application itself?
#SuppressWarnings("SpringJavaAutowiringInspection")
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder
authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService);
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationTokenFilter();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/test").permitAll()
.antMatchers("/api/**").permitAll()
.anyRequest().authenticated();
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
}
I have a custom filter that runs before Spring Security. I want to be able to exclude some URLs (like /test) from the filter and Spring Security and others to be intercepted (like /api/**).
When using postman to test localhost/test it still goes through the filter even though I have antMatchers("/test").permitAll().
How do I bypass the filter?
You can disable the Spring Security filter chain for some URLs, see WebSecurity#ignoring:
Allows adding RequestMatcher instances that should that Spring Security should ignore. Web Security provided by Spring Security (including the SecurityContext) will not be available on HttpServletRequest that match. Typically the requests that are registered should be that of only static resources. For requests that are dynamic, consider mapping the request to allow all users instead.
Example Usage:
webSecurityBuilder.ignoring()
// ignore all URLs that start with /resources/ or /static/
.antMatchers("/resources/**", "/static/**");
Therefore, you can override WebSecurityConfigurerAdapter#configure:
Override this method to configure WebSecurity. For example, if you wish to ignore certain requests.
To ignore path /test you have to add following method to your configuration:
public void configure​(WebSecurity web)
webSecurityBuilder
.ignoring()
.antMatchers("/test");
}
I've got spring security configured as
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = false)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.jee()
.mappableRoles("ROLE1", "ROLE2");
}
}
And then #Secured annotations with roles on the rest endpoints.
Doesn't matter what I do I don't seem to be able to create a custom handler for authorization (i.e. a user logged in successfully but doesn't have the right role to access a particular endpoint) error events.
What I tried was:
An exception handler with #ExceptionHandler(value = AccessDeniedException.class) - doesn't get called. I understand that's by design, ok.
AuthenticationEntryPoint configured as
http.exceptionHandling().authenticationEntryPoint(new RestAuthenticationEntryPoint())
#Component( "restAuthenticationEntryPoint" )
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence( HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException ) throws IOException {
// logging
}
}
-doesn't get called
ApplicationListener - I can see it's getting called on context closed, so it's registered correctly but not called on authorization error.
All I need is a simple handler to log unsuccessful authorization events.
It completely slipped my mind that the allowed roles are listed in web.xml as well for j2ee container authentication to work. So any user without a least one of those roles was just being rejected by the container.
Otherwise the first, simplest, method works fine. Hopefully my mistake will help someone in the future
Swagger works! I can interact with http://localhost:8090/sdoc.jsp and everything is fine.
I add the following to pom.xml...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
I also add the following two files:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
if( !Authenticate.authenticate(name, password) )
return null;
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
Authentication auth = new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
and
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.anyRequest().permitAll()
.antMatchers("/**").authenticated().and()
.formLogin().loginPage("/login").permitAll().and()
.httpBasic()
;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new CustomAuthenticationProvider());
}
}
At this point if I visit the same URL that was previously working I now instead get a response type of "text/plain" and instead of a pretty HTML looking browser I see source code.
If I revert the change and remove the two files from project and remove JAR file it works again.
How do I get Spring Security and Swagger to play nice? What am I doing wrong.
I suspect this is due to Spring-Security's effect on the content-type headers (http://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/headers.html#headers-content-type-options).
From the docs -
Historically browsers, including Internet Explorer, would try to guess the content type of a request using content sniffing. This allowed browsers to improve the user experience by guessing the content type on resources that had not specified the content type. For example, if a browser encountered a JavaScript file that did not have the content type specified, it would be able to guess the content type and then execute it.
The problem with content sniffing is that this allowed malicious users to use polyglots (i.e. a file that is valid as multiple content types) to execute XSS attacks. For example, some sites may allow users to submit a valid postscript document to a website and view it. A malicious user might create a postscript document that is also a valid JavaScript file and execute a XSS attack with it.
Again, from the docs, in order to override the default -
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers()
.contentTypeOptions();
}
}
Wow, I figured it was something along these lines. Thanks so much
When I tried this and it started working
.headers()
.disable()
I narrowed the default contentTypeOptions down to..
.headers()
//.contentTypeOptions() // If this is uncommented it fails.
.xssProtection()
.cacheControl()
.httpStrictTransportSecurity()
.frameOptions()
.and()
I've been working on securing a Restful Service using Spring Security Oauth. I've been banging my head trying to secure the /oauth/token endpoint using SSL and only allowing for POST calls.
I'm using #EnableAuthorizationServer which states
Convenience annotation for enabling an Authorization Server (i.e. an
AuthorizationEndpoint and a TokenEndpoint) in the current application
context, which must be a DispatcherServlet context. Many features of
the server can be customized using #Beans of type
AuthorizationServerConfigurer (e.g. by extending
AuthorizationServerConfigurerAdapter). The user is responsible for
securing the Authorization Endpoint (/oauth/authorize) using normal
Spring Security features (#EnableWebSecurity etc.), but the Token
Endpoint (/oauth/token) will be automatically secured using HTTP Basic
authentication on the client's credentials. Clients must be registered
by providing a ClientDetailsService through one or more
AuthorizationServerConfigurers.
Which is great, but I can't seem to override the token endpoint piece or enforce POST-only calls, like with the intercept-url xml syntax
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore()
}
#Autowired
AuthenticationManager authenticationManager
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient('testApp')
.scopes("read", "write")
.authorities('ROLE_CLIENT')
.authorizedGrantTypes("password","refresh_token")
.secret('secret')
.accessTokenValiditySeconds(7200)
}
}
I secured my Resource server with
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired
private RestAuthenticationEntryPoint authenticationEntryPoint;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.requiresChannel().anyRequest().requiresSecure()
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
.disable()
.headers()
.frameOptions().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated()
}
}
Is there a similar builder syntax for the Authorization Servers TokenEndpoint security that uses requiresChannel?
I ended up creating my own config using
org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration
Since i'm using Spring boot I just autowired the SecurityProperties and added this line for the SSL on the Oauth endpoints
if (this.security.isRequireSsl()) {
http.requiresChannel().anyRequest().requiresSecure();
}
And for the POST requirement
http
.authorizeRequests()
.antMatchers(HttpMethod.POST,tokenEndpointPath).fullyAuthenticated()
.antMatchers(HttpMethod.GET,tokenEndpointPath).denyAll()
Afterwards removed the #EnableAuthorizationServer so it would use my config.