I am using Spring Security 3.2.3.RELEASE
And here is code of my WebSecurityConfigurerAdapter
#Configuration
#EnableWebSecurity
public class WebSecurityContext extends WebSecurityConfigurerAdapter {
private static final Logger log = LogManager.getLogger(WebSecurityContext.class);
#Autowired
private AuthenticationProvider authenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
log.info("Setting AuthenticationManagerBuilder");
auth.authenticationProvider(authenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
log.info("Configuring HttpSecurity");
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/**").hasRole("USER")
.anyRequest().authenticated()
.and()
.httpBasic();
}
}
And result is: HTTP Status 403 - Access is denied
But if I comment this line:
//.antMatchers("/**").hasRole("USER")
Everything works fine. Which means (I guess) something wrong with my roles or HttpSecurity.
So I started debugging. I double check that my UserDetails have two GrantedAuthorities with names: ADMIN, USER.
So any ideas what could cause the problem?
From javadoc of the method hasRole(String role):
Shortcut for specifying URLs require a particular role. If you do not
want to have "ROLE_" automatically inserted see hasAuthority(String).
So you can use hasAuthority or hasAnyAuthority instead
Answering my own question
Apparently Spring Security automatically adds prefix ROLE_ to each role name. So adding ROLE_ prefix to each role name in my DB solved the problem.
Related
I try to do the Java annotation based Spring security configuration. I do this after following a tutorial and have the code as provided,
#Configuration
#EnableWebSecurity
// need to change this to the security directory
#ComponentScan("")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
#Autowired
private MySavedRequestAwareAuthenticationSuccessHandler authenticationSuccessHandler;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication()
.withUser("temporary").password("temporary").roles("ADMIN")
.and()
.withUser("user").password("userPass").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/api/foos").authenticated()
.and()
.formLogin()
.successHandler(authenticationSuccessHandler)
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.and()
.logout();
}
#Bean
public MySavedRequestAwareAuthenticationSuccessHandler mySuccessHandler() {
return new MySavedRequestAwareAuthenticationSuccessHandler();
}
#Bean
public SimpleUrlAuthenticationFailureHandler myFailureHandler() {
return new SimpleUrlAuthenticationFailureHandler();
}
}
The API base for the project I work,
public static final String API_BASE = "/*";
For example, I do the cURL request like,
curl -X GET http://localhost:8080/rest/wallet/wallets | json
I'm not sure about the .antMatchers("/api/foos").authenticated() line in the code. For example, from where the foos is coming and do I need to change it to something like .antMatchers("/foos").authenticated()?
If you are new to programming, its a valid question. But get used to it. All the examples would usually have 'foo' and 'bar' as sample variables, method names etc.
Anyways, the .antMatchers("/api/foos").authenticated() specifies that the pattern URL that matches /api/foo need to be authenticated and then the following handlers should be used.
Change the pattern to your matching one - .antMatchers("/rest/wallet/**") and test your code.
For more reference - read this post : When to use Spring Security`s antMatcher()?
I want to restrict some JSP pages according to the UserRole in my spring boot app
for this i have seen so many examples like:-
http.authorizeRequests().antMatchers("/login").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
but my problem is i cant hardcode the UserRoles like ADMIN or USER because i have new user roles created in different occasions so i can't hardcode the exact user-roles.i have the information about the user-roles that can access a list of jsp pages in my database and here iam using spring security and iam newbie to spring boot and spring security.
edit
my config class is
#Configuration
#EnableWebSecurity
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private UserDetailsService userDetailsService;
#Autowired
CustomAuthHandler customAuthenticationHandler;
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
System.out.println(1);
return new BCryptPasswordEncoder();
}
#Bean
CustomAuthHandler authenticationHandler() {
return new CustomAuthHandler();
}
/*#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/edu/assets/**");
}
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/edu/**","/Login**","/UserSignUP","/organization**","/email_availablity").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/Login").usernameParameter("username").passwordParameter("password")
.defaultSuccessUrl("/index1",true).failureHandler(customAuthenticationHandler).permitAll()
.and()
.logout()
.permitAll()
.and()
.csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
}
Finally i found a answer...
1.Create an interceptor class in your spring boot
public class MyCustomInterceptor implements HandlerInterceptor{
//unimplemented methods comes here. Define the following method so that it
//will handle the request before it is passed to the controller.
#Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response){
//your custom logic here.(for request validating)
return true;
}
}
2.Define a configuration class
#Configuration
public class MyConfig extends WebMvcConfigurerAdapter{
#Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new MyCustomInterceptor()).addPathPatterns("/**");
}
}
thats all now your requests will pass through the logic defined under preHandle() method of MyCustomInterceptor before pass through controller
Firstly, you are validating api not .jsp pages.
#Configuration annotation is used in java file to configure your application instead of xml configuration. From the spring's official doc:
Indicates that a class declares one or more #Bean methods and may be
processed by the Spring container to generate bean definitions and
service requests for those beans at runtime.
So, When you annotated a class with #Configuration the methods are executed just for a time at the very beginning when you start your program. That's why you can't dynamically check UserRoles from the below method:
#Override
protected void configure(HttpSecurity http) throws Exception {
// inner codes
}
So, you have to hard coded(in your .java file or .properties file) if you want to authorize from that method.
The Solution
I have noticed that you are using UserDetailsService for the login purpose. You can modify the loadUserByUsername(String username) method and dynamically check authorization for users.
Steps:
Store the complete user information: login, password and Roles in a database. For this you need 3 Tables: 1. User, 2. Roles and 3. User_Roles. Each time while login, you need to set the UserRoles(roles are read from database) to the UserInfo object.
Then you can add a Interceptor(as a url filter) to your project to check each api(or url) whether Logged user is authorized for the api or not. For this, you can query database with the username for the roles and compare with the requested uri.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private RESTAuthenticationFailureHandler authenticationFailureHandler;
#Autowired
private RESTAuthenticationSuccessHandler authenticationSuccessHandler;
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/hello").permitAll()
.antMatchers("/secure/hello").authenticated()
.and()
.httpBasic()
.realmName("KS TEST")
.and()
.csrf()
.disable();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.formLogin().successHandler(authenticationSuccessHandler);
http.formLogin().failureHandler(authenticationFailureHandler);
http.logout().logoutSuccessUrl("/");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
I have pasted part of above code. I also extended three classes and injected them as bean AuthenticationEntryPoint, SimpleUrlAuthenticationFailureHandler, SimpleUrlAuthenticationSuccessHandler thinking I could extend those and try to get custom error message in case of auth failure. I get the standard spring auth failure in my REST API that works perfectly but I want to define my own class that I want to send as response at auth layer even before the resource endpoint comes into play. Like my own custom class with my own data members. Currently I get the default error in case of wrong auth
{"timestamp":1469955305299,"status":401,"error":"Unauthorized","message":"Bad credentials","path":"/secure/hello"}
This is how i execute the rest call with wrong pwd
//REST API execution example
curl -v -u mickey:cheesee http://localhost:8080/secure/hello
If i give the right pwd things work as expected. However in wrong one, say I want to have a class that I can populate and that becomes json reponse at the auth layer. Can someone tell me what I need to do?
I am looking for a non invasive way to add a captcha filter for certain api calls.
My setup consists of two WebSecurityConfigurerAdapters with one filter each (not the captcha filter):
Internal api ("/iapi" use Filter A on all calls but also ignore some public requests like /authenticate)
External api ("/eapi" use Filter B on all calls)
How can I add a filter before the Spring Security stuff, on public, internal api or external api calls? I don't need the SecurityContext, just need to check for a Captcha in the request headers, forward to filterChain (normal filters) or manually deny access. I tried declaring a filter in web.xml, but that breaks the ability to use dependency injection.
Here is my Spring Security Configuration:
#EnableWebSecurity
public class SpringSecurityConfig {
#Configuration
#Order(1)
#EnableGlobalMethodSecurity(securedEnabled = true)
public static class InternalApiConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private Filter filterA;
public InternalApiConfigurerAdapter() {
super(true);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/public/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/iapi/**")
.exceptionHandling().and()
.anonymous().and()
.servletApi().and()
.authorizeRequests()
.anyRequest().authenticated().and()
.addFilterBefore(filterA, (Class<? extends Filter>) UsernamePasswordAuthenticationFilter.class);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return authenticationManager();
}
}
#Configuration
#Order(2)
#EnableGlobalMethodSecurity(securedEnabled = true)
public static class ExternalApiConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private FilterB filterB;
public ExternalApiConfigurerAdapter() {
super(true);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/external/**")
.exceptionHandling().and()
.anonymous().and()
.servletApi().and()
.authorizeRequests()
.anyRequest().authenticated().and()
.addFilterBefore(filterB, (Class<? extends Filter>) UsernamePasswordAuthenticationFilter.class);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return authenticationManager();
}
}
Update: At the moment I have a working configuration with a filter declared in web.xml. However, it has the drawback of being seperated from the Spring Context (e.g. no autowiring of beans), so I am looking for a better solution leveraging Spring.
Summary: There are two remaining problems:
add a filter for specific urls only - using beforeFilter(...) inside any configuration adds a filter to all urls of that configuration. Antmatchers didn't work. I need something like that: /iapi/captcha/, /external/captcha/, /public/captcha/*.
I have a public api which bypasses Spring Security completely: (web
.ignoring()
.antMatchers("/public/**");). I need to bypass Spring Security but still declare a filter there, using Spring autowiring but not necessarily Spring Security features, since my captcha filter only rejects or forwards calls in a stateless way.
You already have a working configuration with filters A and B inserted before UsernamePasswordAuthenticationFilter so should be easy to add another custom filter.
First, you create the filter, and declare it as a bean, either annotating the class with #Component, or as a #Bean inside a #Configuration class, so it is ready to be injected with #Autowired.
Now you are able to inject it, as filter A and B, and use it. According to the Filter Ordering section in the Spring Security reference documentation, the very first Filter in the chain is ChannelProcessingFilter, so in order to insert the filter before anything else in the Spring Security filter chain, you'd do this:
#Autowired
private CaptchaFilter captchaFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/iapi/**")
.addFilterBefore(captchaFilter, (Class<? extends Filter>) ChannelProcessingFilter.class)
.addFilterBefore(filterA, (Class<? extends Filter>) UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.anyRequest().authenticated();
}
By the way, exceptionHandling() anonymous() and servletApi() aren't needed because when extending WebSecurityConfigurerAdapter, these are already included, except for anonymous() when you actually specify more configuration details, as it states HttpSecurity javadoc
Keep in mind that the Spring Security "entrypoint", the DelegatingFilterProxy still will be executed before your filter, but this component only delegates the request to the first filter in the chain, which in this case would be the CaptchaFilter, so you really would execute your filter before anything else from Spring Security.
But if you still want the captcha filter be executed before the DelegatingFilterProxy, there is no way to do so in the Spring Security configuration, and you need to declare it in the web.xml file.
Update: If you do not desire to include the captcha filter in the other configurations, you can always add a third configuration, and the configurations class would be as follows:
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SpringSecurityConfig {
#Configuration
#Order(1)
public static class CaptchaApiConfigurerAdatper extends WebSecurityConfigurerAdapter {
#Autowired
private CaptchaFilter captchaFilter;
public CaptchaApiConfigurerAdatper() {
super(true);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/public/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatcher("/iapi/captcha**")
.antMatcher("/external/captcha**")
.and()
.addFilterBefore(captchaFilter, (Class<? extends Filter>) ChannelProcessingFilter.class)
.authorizeRequests()
.anyRequest().authenticated();
}
}
#Configuration
#Order(2)
public static class InternalApiConfigurerAdapter extends WebSecurityConfigurerAdapter {
// ommiting code for the sake of clarity
}
#Configuration
#Order(3)
public static class ExternalApiConfigurerAdapter extends WebSecurityConfigurerAdapter {
// ommiting code for the sake of clarity
}
By the way, another tip, you can refactor all the common configuration outside the specific configurations, into the main class, like #EnableGlobalMethodSecurity(securedEnabled = true) the AuthenticationManager, the WebSecurity to skip security for the public, but for those since the main class is not extending anything you should #Autowire the method declarations.
Although there would be one problem with the WebSecurity, if you are ignoring /public/** the matcher for the HttpSecurity with /public/captcha** would be ignored, so i guess, you shouldnt refactor out the WebSecurity and have a different pattern in the CaptchaConfig class so it doesnt overlap.
With spring-cloud Angel.SR3 release I followed example in https://github.com/spring-cloud-samples/sso and things work fine with spring-boot 1.2.6.RELEASE.
However with spring-boot 1.3.0.RC1, the oauth2 stuff has moved into spring-boot itself, and the code below fails to compile because class OAuth2SsoConfigurerAdapter no longer exists.
What is the spring-boot only way to create equivalent configuration?
public static void main(String[] args) {
SpringApplication.run(MainAppApplication.class, args);
}
...
#Component
public static class LoginConfigurer extends OAuth2SsoConfigurerAdapter {
#Override
public void match(RequestMatchers matchers) {
matchers.antMatchers("/dashboard/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/dashboard/**").authorizeRequests().anyRequest()
.authenticated().and().csrf()
.csrfTokenRepository(csrfTokenRepository()).and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
}
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
...
};
}
...
}
You just have to use org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter and carefully use this annotation org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso
I've written carefully because its behaviour depends on where you add it. As stated in the javadoc:
Enable OAuth2 Single Sign On (SSO). If there is an existing WebSecurityConfigurerAdapter provided by the user and annotated with #EnableOAuth2Sso, it is enhanced by adding an authentication filter and an authentication entry point. If the user only has #EnableOAuth2Sso but not on a WebSecurityConfigurerAdapter then one is added with all paths secured and with an order that puts it ahead of the default HTTP Basic security chain in Spring Boot.
Hope that helps!
Turns out not special adapter needed, just the regular WebSecurityConfigurerAdapter does the trick. You cannot tell the code from below if oauth2 SSO is involved, more transparent, sort to speak.
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private SecurityProperties security;
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests()
.antMatchers("/", "/ssologout").permitAll()
.anyRequest().fullyAuthenticated()
.and()
.formLogin()
.loginPage("/login").failureUrl("/login?error")
.permitAll()
.and()
.logout().permitAll();
// #formatter:on
}
}