Java annotation based Spring security config - java

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()?

Related

Sending Custom JSON from Java class in case of auth failure of REST API on spring boot

#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?

Adding a #HandleBeforeSave method to my #RepositoryEventHandler class removes the underlying #Entiry from my REST API

Problem
I am using spring and in the process I have added a #RepositoryEventHandler(User.class) for updates (PUT) when I go to modify a user.
I would like to be able to set who is making the edits to the User.
I created a #HandleBeforeCreate which works fine for HTTP POST's but as soon as I add the #HandleBeforeSave the User REST API is no longer available. I do not see a stack trace being created.
Question
Am I missing something with regards to creating the #HandleBeforeSave
#RepositoryEventHandler
#Component
#RepositoryEventHandler(User.class)
public class SpringDataRestEventHandler {
private final UserRepository userRepository;
#Autowired
public SpringDataRestEventHandler(UserRepository userRepository) {
this.userRepository = userRepository;
}
#HandleBeforeCreate
public void applyUserInformationUsingSecurityContext(User user) throws {
String name = SecurityContextHolder.getContext().getAuthentication().getName();
User manager = this.userRepository.findByUserName(name);
if (!manager.hasRole("ROLE_MANAGER")) {
throw new Exception("No manager found for user on applyUserInformationUsingSecurityContext.");
}
user.setManager(name);
}
#HandleBeforeSave
public void applyManagerFromSecurityContext(User user) {
System.out.println("calling before save");
}
}
SecurityConfiguration
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private SpringDataJpaUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(this.userDetailsService)
.passwordEncoder(MCBPasswordEncoder.PASSWORD_ENCODER);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/built/**", "/main.css").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.defaultSuccessUrl("/", true)
.permitAll()
.and()
.httpBasic()
.and()
.csrf().disable() // TODO enable for production
.logout()
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.logoutSuccessUrl("/");
}
}
In the end the problem was actually related to the 2 repositories I created for the User #Entity. I was getting weird results where the API would show up (with the one repo) and disappear with the other repo.
I have since fixed this by
Use only one repo instead of two Extend Repository instead that
JPARepository
Copy and paste methods that i needed from PagingAndSortingRepository.
Added #PreAuthorize accordingly to specific methods, not to
the class. This was the initial problem as I split it out when I wanted to manipulate the repo outside of the REST api.

How to provide custom security configuration for oauth2 with spring-boot 1.3.0.RC1

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
}
}

Unexpected Spring Boot auth behaviour

I want to secure all endpoint except /register and /activate, but the following configuration does not permit unauthenticated calls to the excluded endpoints. What's missing/wrong? I compared it to several tutorials and code snippets but I am not able to find the failure. I have to remove the .anyRequest().authenticated() part to be able to call one of the two endpoints, but then all other endpoints are also unsecured.
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/register", "/activate").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable()
.httpBasic();
}
#Autowired
public void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}

Basic and form based authentication with Spring security Javaconfig

I'm trying to define two different security configurations for different url patterns, one of them using form login and another one using basic authentication for an api.
The solution I'm looking for is similar to the one explained here http://meera-subbarao.blogspot.co.uk/2010/11/spring-security-combining-basic-and.html but I would like to do it using java config.
Thanks in advance.
This is the configuration I currently have:
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
#Override
public void configure(WebSecurity web) throws Exception {
// Ignore any request that starts with "/resources/".
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls().antMatchers("/", "/index", "/user/**", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and().formLogin()
.loginUrl("/login")
.failureUrl("/login-error")
.loginProcessingUrl("/security_check")
.usernameParameter("j_username").passwordParameter("j_password")
.permitAll();
http.logout().logoutUrl("/logout");
http.rememberMe().rememberMeServices(rememberMeServices()).key("password");
}
#Bean
public RememberMeServices rememberMeServices() {
TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices("password", userService);
rememberMeServices.setCookieName("cookieName");
rememberMeServices.setParameter("rememberMe");
return rememberMeServices;
}
}
The solution I found was to create another class extending WebSecurityConfigurerAdapter inside the first one, like is described https://github.com/spring-projects/spring-security-javaconfig/blob/master/samples-web.md#sample-multi-http-web-configuration
My solution is as follows:
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
#Override
public void configure(WebSecurity web) throws Exception {
// Ignore any request that starts with "/resources/".
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls().antMatchers("/", "/index", "/user/**", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and().formLogin()
.loginUrl("/login")
.failureUrl("/login-error")
.loginProcessingUrl("/security_check")
.usernameParameter("j_username").passwordParameter("j_password")
.permitAll();
http.logout().logoutUrl("/logout");
http.rememberMe().rememberMeServices(rememberMeServices()).key("password");
}
#Bean
public RememberMeServices rememberMeServices() {
TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices("password", userService);
rememberMeServices.setCookieName("cookieName");
rememberMeServices.setParameter("rememberMe");
return rememberMeServices;
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("api").password("pass").roles("API");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls()
.antMatchers("/api/**").hasRole("API")
.and()
.httpBasic();
}
}
}
I would say by simply doing it. Specify a second line with authorizeUrls() but for your URLs that are needed with basic authentication. Instead of formLogin() use httpBasic()
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls().antMatchers("/", "/index", "/user/**", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and().formLogin()
.loginUrl("/login")
.failureUrl("/login-error")
.loginProcessingUrl("/security_check")
.usernameParameter("j_username").passwordParameter("j_password")
.permitAll();
http.authorizeUrls().antMatchers("/api/*").hasRole("YOUR_ROLE_HERE").and().httpBasic();
http.logout().logoutUrl("/logout");
http.rememberMe().rememberMeServices(rememberMeServices()).key("password");
}
Something like that should work.
Links: HttpSecurity, HttpBasicConfgurer.
You can solve it by adding .antMatcher("/api/**") just after http in your first config to manage only /api urls. You must have it on the first adapter:
http
.antMatcher("/api/*")
.authorizeRequests()
.antMatchers("^/api/.+$").hasRole("ADMIN")
....
Obviously As Spring updates these tend to fade in their applicability. Running spring cloud starter security 1.4.0.RELEASE this is my solution. My use case was a bit different as I'm trying to secure the refresh endpoint using basic auth for cloud configuration and using a gateway with spring session to pass the authentication in all other instances.
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal1(AuthenticationManagerBuilder auth) throws Exception {
//try in memory auth with no users to support the case that this will allow for users that are logged in to go anywhere
auth.inMemoryAuthentication();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/user").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable();
}
#Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder(11);
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationProvider customAuthenticationProvider;
#Autowired
protected void configureGlobal2(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(customAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/refresh").hasRole("SUPER")
.and()
.csrf().disable();
}
}
}
see the spring security docs for further clarification: Spring Security
The basic idea is that the #Order annotation will dictate what order the auth schemes are run. No #Order means that it is last. If the authorizeRequests() section cannot match on the incoming URL then that configuration will pass and the next one will attempt authentication. This will proceed until authentication succeeds or fails.
The other answers in here are relatively old and for example, I can't find authorizeUrls in Spring 4.
In SpringBoot 1.4.0 / Spring 4, I've implemented the basic/form login like this:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
protected void configure (HttpSecurity aHttp) throws Exception
{
aHttp.authorizeRequests ().antMatchers (("/api/**")).fullyAuthenticated ().and ().httpBasic ();
aHttp.formLogin ()
.loginPage ("/login").permitAll ()
.and ().logout ().permitAll ();
}
}
There may be more ellegant ways of writing this - I'm still working on understanding how this builder works in terms of sequence and so forth. But this worked.

Categories

Resources