I have a problem concerning Spring Boot role based authentication. Basically, I would like to have users and admins and I want to prevent users from accessing admin resources. So I created a SecurityConfig class:
package test;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1").password("password1").roles("USER, ADMIN")
.and()
.withUser("user2").password("password2").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/service/test").access("hasRole('USER') or hasRole('ADMIN')")
.antMatchers("/service/admin").access("hasRole('ADMIN')");
}
}
This is my little REST service:
package test;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/service")
public class RestService {
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String echo() {
return "This is a test";
}
#RequestMapping(value = "/admin", method = RequestMethod.GET)
public String admin() {
return "admin page";
}
}
And my Application class:
package test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Unfortunately, I always get a 403 "forbidden/access denied" error message when executing "curl user1:password1#localhost:8080/service/admin"... Did I miss anything in the configure method?
Thank you very much in advance!
Can you please check this.
withUser("user1").password("password1").roles("USER", "ADMIN")
write "USER" and "ADMIN" in separate qoutes.
I changed it in the following way, now it seems to be working:
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1").password("password1").roles("USER", "ADMIN")
.and()
.withUser("user2").password("password2").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().permitAll()
.and()
.authorizeRequests()
.antMatchers("/service/test").hasAnyRole("USER", "ADMIN")
.antMatchers("/service/admin").hasRole("ADMIN")
.anyRequest().authenticated();
}
}
Thank you very much for your answers!
following setup works fine for my spring boot app:
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()//allow CORS option calls
.antMatchers("/home", "/").hasAnyAuthority(Role.ROLE_ADMIN, Role.ROLE_USER)
.antMatchers("/admin").hasAuthority(Role.ROLE_ADMIN)enter code here
Related
I am configuring spring security in my project using jwt token.
I am sucessfully generated jwt token and accessing it from front end.
In my spring boot REST APT I have several controllers with all CRUD methods.
I want to give access of get method to all the users and even to public, while
for POST,PUT and Delete I want to give access to only admin and moderator depending on the case.
But for some POST method like in inquiry form i want to give access to all users.
What approach should I follow for that,
Do i need to write
#PreAuthorize("hasRole('USER') or hasRole('MODERATOR') or hasRole('ADMIN')")
for each method in each controller.
Right now i just build a test page to check access of roles .
package com.panchmeru_studio.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#CrossOrigin(origins = "*", maxAge = 3600)
#RestController
#RequestMapping("/api/test")
public class TestController {
#GetMapping("/all")
public String allAccess() {
return "Public Content.";
}
#GetMapping("/user")
#PreAuthorize("hasRole('USER') or hasRole('MODERATOR') or hasRole('ADMIN')")
public String userAccess() {
return "User Content.";
}
#GetMapping("/mod")
#PreAuthorize("hasRole('MODERATOR')")
public String moderatorAccess() {
return "Moderator Board.";
}
#GetMapping("/admin")
#PreAuthorize("hasRole('ADMIN')")
public String adminAccess() {
return "Admin Board.";
}
}
Securityconfig.java
package com.panchmeru_studio.security.jwt;
import com.panchmeru_studio.filter.AuthTokenFilter;
import com.panchmeru_studio.security.service.ApplicationUserDetailsService;
import com.panchmeru_studio.security.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.server.authorization.AuthorizationWebFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import static com.panchmeru_studio.constants.SecurityConstants.SIGN_UP_URL;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private MyUserDetailsService userDetailsService;
// private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private AuthEntryPointJwt unauthorizedHandler;
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
public SecurityConfiguration(MyUserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
// this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/test/**").permitAll()
.anyRequest().authenticated().and()
// .addFilter(new AuthenticationFilter(authenticationManager()))
// .addFilter(new AuthorizationFilter(authenticationManager()))
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler);
// .authorizeRequests().antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
// .anyRequest().authenticated()
// .and()
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Now for example i have 10 controllers, Project ,AboutUS , ProjectCategory,Gallery
Each has different url(Request mapping) so, Do i need to assign #PreAuthorize to each method to each controller and then give request mapping of each controller to security config to give authrization?
If you want to base the access to methods only on user roles, then the approach you described is the correct one.
Since you're using JWTs to authorize access to your APIs, you could secure the APIs based on claims in JWTs instead of the user profile. In such a setup you don't need access to user accounts at all, you make all the decisions based on what is in the JWT. Have a look at this example to see how to set this up in a Spring API.
I am trying to secure a REST endpoint via the #Secured annotation of Spring Security. My main application (Spring Boot App) with the security config and the rest controller are in different packages and project.
Main app package: com.myapp.api.web
Rest controller packge: com.myapp.api.rest
Mainapp:
#SpringBootApplication
#ComponentScan(basePackages = "com.myapp.api")
#EntityScan("com.myapp.api")
#RestController
public class ApiApplication extends SpringBootServletInitializer
{
public static void main(String[] args)
{
SpringApplication.run(ApiApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
{
return application.sources(ApiApplication.class);
}
}
Security Config:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true,
securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
private static final String USERS_CONFIG_FILE_NAME = "users.yml";
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.httpBasic()
.and()
.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth,
InMemoryUserDetailsManager inMemoryUserDetailsManager, PasswordEncoder passwordEncoder) throws Exception
{
auth.userDetailsService(inMemoryUserDetailsManager).passwordEncoder(passwordEncoder);
}
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() throws IOException
{
return new InMemoryUserDetailsManager(
PropertiesLoaderUtils.loadAllProperties(USERS_CONFIG_FILE_NAME, getClass().getClassLoader()));
}
}
Rest controller:
#RestController
public class RestController
{
private final RestService service;
#PostMapping("/rest/v1")
#Secured({"ROLE_ADMIN"})
public List<String> getStates(#RequestBody List<String> Ids)
{
...
}
My rest endpoint is working as long as I am not setting securedEnabled = true. After setting it true I am getting a 404 Not Found as respond message. I've already debugged it and found out that the Spring Security somewhen stops in the filter chain and that the request never reaches the controller.
As far as I tested it, as long as the rest controller is in a different project this error will occure. After moving it to the same project it is working as it should.
Is there something missing in my Securityconfig or what could the problem be?
I am able to get values by your code , I have only changed Password Encoder to default and changed inMemoryAuthentication .
I did it as I don't have your file "users.yml" If you can share a sample , we will look in it , But below is my code. I kept all logic in 2 files just to verify.
Configuration Class
package com.myapp.api.web.Api;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true,
securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
private static final String USERS_CONFIG_FILE_NAME = "users.yml";
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.httpBasic()
.and()
.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new PasswordEncoder() {
#Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
#Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.toString().equals(encodedPassword);
}
};
}
#Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.inMemoryAuthentication()
.withUser("user").password("user").roles("USER")
.and().withUser("admin").password("admin").roles("ADMIN");
}
/*
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() throws IOException
{
return new InMemoryUserDetailsManager(
PropertiesLoaderUtils.loadAllProperties(USERS_CONFIG_FILE_NAME, getClass().getClassLoader()));
}*/
}
Main Class
package com.myapp.api.web.Api;
import java.util.List;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
#ComponentScan(basePackages = "com.myapp.api")
#RestController
#SpringBootApplication
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
#PostMapping("/rest/v1")
#Secured({"ROLE_ADMIN"})
public List<String> getStates(#RequestBody List<String> Ids)
{
return Ids;
// ...
}
}
I am trying to test my web api thats secured using the standard Spring Security API,However whenever I login to my application the /test.html api keeps returning a 302 redirect.
username:admin /
password:admin
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#RequestMapping("/hello")
public String hello() {
return "hello";
}
}
package com.example.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
#EnableWebSecurity
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/test.html").permitAll()
.loginProcessingUrl("/user/login")
.and()
.authorizeRequests()
.antMatchers("/test.html").permitAll()
.anyRequest()
.authenticated();
}
}
package com.example.demo;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
#Component
public class UserDetailsServiceImpl implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return new User("admin",
"$2a$10$vs7veyVUaqeGyVlxXpp94O7BcmzcF2HGUmH2va6XDVCj2mK8uFzRi",
AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
}
https://github.com/woshituotuo/demo.git
You need put after the .hasAnyRole and put the user's role
after put these you need put .anyRequest().authenticated()
and that is all
done
Cross-site request forgery
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/test.html").permitAll()
.loginProcessingUrl("/user/login")
.and()
.authorizeRequests()
.antMatchers("/test.html").permitAll()
.anyRequest()
.authenticated()
.and() +
.csrf() +
.disable(); +
}
Exclude SecurityAutoConfiguration.class and ManagementWebSecurityAutoConfiguration.class from the main class of my spring boot application works for me.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
#SpringBootApplication(exclude = {SecurityAutoConfiguration.class,
ManagementWebSecurityAutoConfiguration.class})
public class MainApplication implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
I have setup a auth server and resource server as mentioned in the below article
http://www.hascode.com/2016/03/setting-up-an-oauth2-authorization-server-and-resource-provider-with-spring-boot/
I downloaded the code and it is working fine. Now the issue is that in the resource provider project there is only one RestController annotated class as shown below
package com.hascode.tutorial;
import java.util.UUID;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Scope;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
#RestController
#EnableResourceServer
public class SampleResourceApplication {
public static void main(String[] args) {
SpringApplication.run(SampleResourceApplication.class, args);
}
#RequestMapping("/")
public String securedCall() {
return "success (id: " + UUID.randomUUID().toString().toUpperCase() + ")";
}
}
Now when I create a different class annotated with #RestController as shown below
#RestController
#RequestMapping("/public")
public class PersonController {
#Autowired
private PersonRepository personRepo;
#RequestMapping(value = "/person", method = RequestMethod.GET)
public ResponseEntity<Collection<Person>> getPeople() {
return new ResponseEntity<>(personRepo.findAll(), HttpStatus.OK);
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<Person> getPerson(#PathVariable long id) {
Person person = personRepo.findOne(id);
if (person != null) {
return new ResponseEntity<>(personRepo.findOne(id), HttpStatus.OK);
} else {
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
}
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> addPerson(#RequestBody Person person) {
return new ResponseEntity<>(personRepo.save(person), HttpStatus.CREATED);
}
#RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public ResponseEntity<Void> deletePerson(#PathVariable long id, Principal principal) {
Person currentPerson = personRepo.findByUsername(principal.getName());
if (currentPerson.getId() == id) {
personRepo.delete(id);
return new ResponseEntity<Void>(HttpStatus.OK);
} else {
return new ResponseEntity<Void>(HttpStatus.UNAUTHORIZED);
}
}
#RequestMapping(value = "/{id}/parties", method = RequestMethod.GET)
public ResponseEntity<Collection<Party>> getPersonParties(#PathVariable long id) {
Person person = personRepo.findOne(id);
if (person != null) {
return new ResponseEntity<>(person.getParties(), HttpStatus.OK);
} else {
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
}
}
but when I tried to access the service (http://localhost:9001/resources/public/person) I am getting 404
{
"timestamp": 1508752923085,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/resources/public/person"
}
when I access http://localhost:9001/resources/ I am getting the correct result like
success (id: 27DCEF5E-AF11-4355-88C5-150F804563D0)
Should I register the Contoller anywherer or am I missing any configuration
https://bitbucket.org/hascode/spring-oauth2-example
UPDATE 1
ResourceServerConfiguration.java
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().and()
.authorizeRequests()
.antMatchers("/resources/public/**").permitAll()
.antMatchers("/resources/private/**").authenticated();
}
}
OAuth2SecurityConfiguration.java
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().and()
.authorizeRequests()
.antMatchers("/resources/public/**").permitAll()
.antMatchers("/resources/private/**").authenticated();
}
}
UPDATE 2
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/resources/public/**").permitAll() //Allow register url
.anyRequest().authenticated().and()
.antMatcher("/resources/**").authorizeRequests() //Authenticate all urls with this body /api/home, /api/gallery
.antMatchers("/resources/**").hasRole("ADMIN")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler()); //This is optional if you want to handle exception
}
Make your new controller PersonController discoverable by Spring Boot either by using #ComponentScan on a configuration class or by moving PersonController to a package in or under your main class annotated with #SpringBootApplication.
Second fix your OAuth2SecurityConfiguration class like so
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll(); //This will permit the (oauth/token) url for getting access token from the oauthserver.
}
}
Now fix your resource server like so
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/v1/register", "/api/v1/publicOne", "/api/v1/publicTwo").permitAll() //Allow urls
.anyRequest().authenticated().and()
.antMatcher("/api/**").authorizeRequests() //Authenticate all urls with this body /api/home, /api/gallery
.antMatchers("/api/**").hasRole("ADMIN")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler()); //This is optional if you want to handle exception
}
}
Find the complete source code here. Hope this helps.
Note: You can customize your urls based on the above answer.
Why your request url is http://localhost:9001/resources/public/person
I think it should be like http://localhost:9001/public/person
I have a trouble with SecurityContextHolder.getContext().getAuthentication() which is null. I have tried a lot of combination with annotations and examples. (code from site does not work in my application, do not know why yet).
So for now I get org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext. If you look at sources you spot that getAuthentification is delegated to SecurityContextHolderStrategy which thread local field and populated during SecurityContextHolder initialization. Anybody know when spring security should "populate" it with authentification? (in servlet filter, before method invocation, etc.)
UPDATED
Security configuration is:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
#EnableGlobalAuthentication
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/rest/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
RestController
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class SecurityChecker {
#PreAuthorize("isAuthenticated()")
#RequestMapping("/allow")
public String allow() {
return "{\"status\" : \"ok\"}";
}
#PreAuthorize("isAnonymous()")
#RequestMapping("/anonymous")
public String anonymous() {
return "{\"status\" : \"anonymous\"}";
}
}
Application initializer
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{AppConfiguration.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SecurityConfiguration.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/rest/*"};
}
AppConfiguration contains some code for data source, entityManager and transactionManager config for sprng data rest.
Request to /rest/allow url result in exception org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
Note
Form authorization config may be not correct, I tried to replace it with basic auth, but anyway I should get unauthorized response instead fo exception.
Versions
Spring is 4.0.5.RELEASE, spring security is 4.0.2.RELEASE.
The solution for fixing spring security was very simple just add:
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {}
and move SecurityConfiguration.class to getRootConfigClasses() method.
And everything works! :)