401 instead of 403 with Spring Boot 2 - java

With Spring Boot 1.5.6.RELEASE I was able to send HTTP Status code 401 instead of 403 as described in How let spring security response unauthorized(http 401 code) if requesting uri without authentication, by doing this:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//...
http.exceptionHandling()
.authenticationEntryPoint(new Http401AuthenticationEntryPoint("myHeader"));
//...
}
}
using the org.springframework.boot.autoconfigure.security.Http401AuthenticationEntryPoint class.
I just upgraded to Spring Boot 2.0.0.RELEASE and found there is not such class any more (at least in that package).
Questions:
Does this class (Http401AuthenticationEntryPoint) exist yet in Spring Boot?
If no, what could be a good alternative for keeping the same behavior in an existing project in order to keep consistency with other implementations which depend on this status code (401) instead of 403?
Please notice this is different from Spring Security anonymous 401 instead of 403 because it's referring specifically to SpringBoot 2 (there are solutions in that post not applicable anymore in SpringBoot version 2 or others are not needed at all)

Heads up
By default Spring Boot 2 will return 401 when spring-boot-starter-security is added as a dependency and an unauthorized request is performed.
This may change if you place some custom configurations to modify the security mechanism behavior. If that's the case and you truly need to force the 401 status, then read the below original post.
Original Post
The class org.springframework.boot.autoconfigure.security.Http401AuthenticationEntryPoint was removed in favor of org.springframework.security.web.authentication.HttpStatusEntryPoint.
In my case the code would go like this:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//...
http.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
//...
}
}
Bonus
If you need to return some information in the response body or customize the response somehow you can do something like this:
1- Extend AuthenticationEntryPoint
public class MyEntryPoint implements AuthenticationEntryPoint {
private final HttpStatus httpStatus;
private final Object responseBody;
public MyEntryPoint(HttpStatus httpStatus, Object responseBody) {
Assert.notNull(httpStatus, "httpStatus cannot be null");
Assert.notNull(responseBody, "responseBody cannot be null");
this.httpStatus = httpStatus;
this.responseBody = responseBody;
}
#Override
public final void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
response.setStatus(httpStatus.value());
try (PrintWriter writer = response.getWriter()) {
writer.print(new ObjectMapper().writeValueAsString(responseBody));
}
}
}
2- Provide an instance of MyEntryPoint to the security configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// customize your response body as needed
Map<String, String> responseBody = new HashMap<>();
responseBody.put("error", "unauthorized");
//...
http.exceptionHandling()
.authenticationEntryPoint(new MyEntryPoint(HttpStatus.UNAUTHORIZED, responseBody));
//...
}
}

Just to elaborate #lealceldeiro's answer:
Before Spring Boot 2 my Securiy Configuration class looked like this:
#Configuration
public class MyConfig extends WebSecurityConfigurerAdapter {
#Bean
public Http401AuthenticationEntryPoint securityException401EntryPoint() {
return new Http401AuthenticationEntryPoint("Bearer realm=\"webrealm\"");
}
#Autowired
private Http401AuthenticationEntryPoint authEntrypoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
// some http configuration ...
// Spring Boot 1.5.x style
http.exceptionHandling().authenticationEntryPoint(authEntrypoint);
}
//...
}
And now in Spring Boot 2 it looks like this:
#Configuration
public class MyConfig extends WebSecurityConfigurerAdapter {
//Bean configuration for Http401AuthenticationEntryPoint can be removed
//Autowiring also removed
#Override
protected void configure(HttpSecurity http) throws Exception {
// some http configuration ...
// Spring Boot 2 style
http.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
}
//...
}
See also this comment in Spring Boot Github Repo > PR Remove Http401AuthenticationEntryPoint.

Http401AuthenticationEntryPoint was removed.
See Spring Boot Github Repo > Issue #10715 (Remove Http401AuthenticationEntryPoint):
Remove Http401AuthenticationEntryPoint
rwinch commented on 20 Oct 2017
As far as I can tell it is not being used in the Spring Boot code base, so it might be good to remove Http401AuthenticationEntryPoint.
Depending on your requirements, you could use:
HttpStatusEntryPoint
BasicAuthenticationEntryPoint

For reactive (WebFlux) stack you can override the returned status code by adding such #Bean to catch some specific exceptions:
#Component
class MyErrorAttributes : DefaultErrorAttributes() {
override fun getErrorAttributes(
request: ServerRequest,
options: ErrorAttributeOptions
): MutableMap<String, Any> {
val cause = super.getError(request)
val errorAttributes = super.getErrorAttributes(request, options)
when (cause) {
is TokenExpiredException -> {
errorAttributes["status"] = HttpStatus.UNAUTHORIZED.value()
errorAttributes["error"] = HttpStatus.UNAUTHORIZED.reasonPhrase
}
}
return errorAttributes
}
}

You can customize your logic with overriding the class AuthenticationEntryPoint
this should be working:
#Component public class AuthEntryPointException implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = -8970718410437077606L;
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.setStatus(HttpStatus.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.getWriter().write("{\"result\":\"UNAUTHORIZED\",\"message\":\"UNAUTHORIZED or Invalid Token\"}");
}
}

Related

Providing way to configure spring security?

Is it possible to configure Spring security in a way that it reads configuration details from an external file and configures accordingly ?
(I am not talking about changing config at runtime, I am talking about reading from a file at the time of startup).
An example of my existing Spring security config :
#EnableWebSecurity
#Configuration
public class SecurityConfig {
#Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("userPass").roles("USER").build());
manager.createUser(User.withUsername("admin").password("adminPass").roles("ADMIN").build());
return manager;
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().withUser("user").password("user").roles("USER");
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
}
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/v1/**")
.authorizeRequests()
.antMatchers("/api/v1/**").authenticated()
.and()
.httpBasic();
}
}
#Configuration
#Order(2)
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().withUser("user1").password("user").roles("USER");
auth.inMemoryAuthentication().withUser("admin1").password("admin").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/test/**")
.authorizeRequests()
.antMatchers("/api/test/**").authenticated()
.and()
.formLogin();
}
}
}
As you can see, I am using multiple configurations (have a look at Order() annotation). What I want to be able to do is decide at the time of startup, the number and types of configuration. For example a first client may want to have 2 configs (e.g.LdapConfig and SamlConfig), a second one may want LdapConfig and SqlConfig and a third one may want 4-5 configs. Is it possible to do that?
NOTE: I am not using Spring Boot
EDIT
Summary of why I want in this way :
By customer I mean the company that will be buying my product. And by users I mean the actual end users of the company that bought my product. So I shipped the product to 3 companies. First will configure it to have ldap auth flow and google-oauth2 auth flow. Users of this first company will be seeing a login page with these 2 options. Company 2 now might have a ldap auth flow and saml auth flow and users of that company will be seeing those 2 options. And the company is selecting the available options before startup.
You could load properties, e.g. DB credentials, before creating your WebApplicationContext. Look at the following example:
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
// Tell the EnvironmentManager to load the properties. The path to the config
// file is set by Tomcat's home variable. If you change the container you might
// need to change this, too.
EnvironmentParamManager.initialize(System.getProperty("catalina.home"));
// now create the Spring Context
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(RootConfig.class);
rootContext.setServletContext(servletContext);
SpringApplicationContextProvider.configure(rootContext);
// ... other config
}
The EnvironmentParamManager could look like this. I've decided to make it static so that the properties are accessible from everywhere even in non-Spring parts of the application.
public class EnvironmentParamManager {
private static Properties properties = new Properties();
public static void initialize(String pathToConfigFile) {
BufferedInputStream stream;
try {
stream = new BufferedInputStream(new FileInputStream(
pathToConfigFile + "myconfig.props"));
properties.load(stream);
stream.close();
} catch (Throwable e) {
throw new Error("Cannot read environment settings from file " + pathToConfigFile);
}
}
public static String getMongoDBHostname() {
return properties.getProperty("mongodb.username");
}
}
When using JavaConfig, you can access your config properties at the Bean creation phase easily like this
#Configuration
public class CoreConfig {
#Bean
public MongoDbFactory mongoDbFactory() throws Exception {
...
ServerAddress address = new
ServerAddress(EnvironmentParamManager.getMongoDBHost(),
EnvironmentParamManager.getMongoDBPort());
...
}
Of course, you are free to connect to any other services like LDAP etc. in just the same way as you load the local properties file before the Spring Context is bootstrapped. Hope that helps.
Selective loading of components can be achived with Springs #Conditional annotation.
The configs would look like this:
#Configuration(value = "some.security.config")
#Conditional(value = LoadSecurityConfigCondition.class)
public class SomeSecurityConfig {
// some code
}
#Configuration(value = "other.security.config")
#Conditional(value = LoadSecurityConfigCondition.class)
public class OtherSecurityConfig {
// other code
}
Then, the LoadSecurityConfigCondition.class decides if the components are loaded:
#Component
public class LoadSecurityConfigCondition implements Condition {
#Override
public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
boolean enabled = false;
if (metadata.isAnnotated(Configuration.class.getName())) {
final String name = (String) metadata.getAnnotationAttributes(Configuration.class.getName()).get("value");
if (StringUtils.isNotBlank(name)) {
/* Here you may load your config file and
* retrieve the information on wether to load
* the config identified by its name.
*/
enabled = ...;
}
}
return enabled;
}
}
In this example, the config entries can now be created with the #Configuration name, postfixed with .enabled to clarify its purpose:
some.security.config.enabled=true
other.security.config.enabled=false
Have you tried this:
#EnableWebSecurity
#Configuration
public class SecurityConfig {
#Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new MemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("userPass").roles("USER").build());
manager.createUser(User.withUsername("admin").password("adminPass").roles("ADMIN").build());
return manager;
}
#Configuration
#Profile({"profile1", "profile2"})
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().withUser("user").password("user").roles("USER");
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
}
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/v1/**")
.authorizeRequests()
.antMatchers("/api/v1/**").authenticated()
.and()
.httpBasic();
}
}
#Configuration
#Profile("profile1")
#Order(2)
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().withUser("user1").password("user").roles("USER");
auth.inMemoryAuthentication().withUser("admin1").password("admin").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/test/**")
.authorizeRequests()
.antMatchers("/api/test/**").authenticated()
.and()
.formLogin();
}
}
}
So with spring.profiles.active=profile1, both configurations are loaded, with spring.profiles.active=profile2, only the first configuration is loaded. Of course, you can use more than 2 profiles, and you can also activate more than one profile at startup (also comma separated). You just need to divide your configurations and profiles in a way that fits your requirements.

Use #WithMockUser (with #SpringBootTest) inside an oAuth2 Resource Server Application

Environment:
I have a spring boot based microservice architecture application consisting of multiple infrastructural services and resource services (containing the business logic). Authorization and authentication is handled by an oAuth2-Service managing the user entities and creating JWT tokens for the clients.
To test a single microservice application in its entirety i tried to build tests with testNG, spring.boot.test, org.springframework.security.test ...
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, properties = {"spring.cloud.discovery.enabled=false", "spring.cloud.config.enabled=false", "spring.profiles.active=test"})
#AutoConfigureMockMvc
#Test
public class ArtistControllerTest extends AbstractTestNGSpringContextTests {
#Autowired
private MockMvc mvc;
#BeforeClass
#Transactional
public void setUp() {
// nothing to do
}
#AfterClass
#Transactional
public void tearDown() {
// nothing to do here
}
#Test
#WithMockUser(authorities = {"READ", "WRITE"})
public void getAllTest() throws Exception {
// EXPECT HTTP STATUS 200
// BUT GET 401
this.mvc.perform(get("/")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
}
}
where the security (resource server) config is the following
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
// get the configured token store
#Autowired
TokenStore tokenStore;
// get the configured token converter
#Autowired
JwtAccessTokenConverter tokenConverter;
/**
* !!! configuration of springs http security !!!
*/
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/**").authenticated();
}
/**
* configuration of springs resource server security
*/
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
// set the configured tokenStore to this resourceServer
resources.resourceId("artist").tokenStore(tokenStore);
}
}
and the following method based security check annotated inside the controller class
#PreAuthorize("hasAuthority('READ')")
#RequestMapping(value = "/", method = RequestMethod.GET)
public List<Foo> getAll(Principal user) {
List<Foo> foos = fooRepository.findAll();
return foos;
}
I thought that would work but when running the test i only get an assertion error
java.lang.AssertionError: Status
Expected :200
Actual :401
Question:
Is there something totally obvious that i am doing wrong? Or is #WithMockUser not going to work with #SpringBootTest and #AutoConfigureMockMvc in an oAuth2 environment? If this is the case... what would be the best approach for testing route and method based security configurations as part of such an (integration) test like this one?
Appendix:
I also tried different approaches like something like the following... but it led to the same result :(
this.mvc.perform(get("/")
.with(user("admin").roles("READ","WRITE").authorities(() -> "READ", () -> "WRITE"))
.accept(MediaType.APPLICATION_JSON))
see:
spring security testing
spring boot 1.4 testing
#WithMockUser creates the authentication in SecurityContext.
Same applies for with(user("username")).
By default the OAuth2AuthenticationProcessingFilter does not use the SecurityContext, but always build the authentication from the token ("stateless").
You can easily change this behavior be setting the stateless flag in the resource server security configuration to false:
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration implements ResourceServerConfigurer {
#Override
public void configure(ResourceServerSecurityConfigurer security) throws Exception {
security.stateless(false);
}
#Override
public void configure(HttpSecurity http) {}
}
Another option is to extend ResourceServerConfigurerAdapter, but the problem with that is that it comes with configuration that forces all requests to be authenticated. Implementing the interface leaves your main security config unchanged apart from the statelessness.
Of course, set the flag to to false in your test contexts, only.
I had de same issue, and the only way I found was creating a token and using it in the mockMvc perform
mockMvc.perform(get("/resource")
.with(oAuthHelper.bearerToken("test"))
And the OAuthHelper:
#Component
#EnableAuthorizationServer
public class OAuthHelper extends AuthorizationServerConfigurerAdapter {
#Autowired
AuthorizationServerTokenServices tokenservice;
#Autowired
ClientDetailsService clientDetailsService;
public RequestPostProcessor bearerToken(final String clientid) {
return mockRequest -> {
OAuth2AccessToken token = createAccessToken(clientid);
mockRequest.addHeader("Authorization", "Bearer " + token.getValue());
return mockRequest;
};
}
OAuth2AccessToken createAccessToken(final String clientId) {
ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
Collection<GrantedAuthority> authorities = client.getAuthorities();
Set<String> resourceIds = client.getResourceIds();
Set<String> scopes = client.getScope();
Map<String, String> requestParameters = Collections.emptyMap();
boolean approved = true;
String redirectUrl = null;
Set<String> responseTypes = Collections.emptySet();
Map<String, Serializable> extensionProperties = Collections.emptyMap();
OAuth2Request oAuth2Request = new OAuth2Request(requestParameters, clientId, authorities,
approved, scopes, resourceIds, redirectUrl, responseTypes, extensionProperties);
User userPrincipal = new User("user", "", true, true, true, true, authorities);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userPrincipal, null, authorities);
OAuth2Authentication auth = new OAuth2Authentication(oAuth2Request, authenticationToken);
return tokenservice.createAccessToken(auth);
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("test")
.authorities("READ");
}
}
As I was specifically trying to write tests against our ResourceServerConfiguration, I worked around the issue by creating a test wrapper for it which set security.stateless to false:
#Configuration
#EnableResourceServer
public class ResourceServerTestConfiguration extends ResourceServerConfigurerAdapter {
private ResourceServerConfiguration configuration;
public ResourceServerTestConfiguration(ResourceServerConfiguration configuration) {
this.configuration = configuration;
}
#Override
public void configure(ResourceServerSecurityConfigurer security) throws Exception {
configuration.configure(security);
security.stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
configuration.configure(http);
}
}

Spring security - Custom AuthenticationProvider not working - Java Config

I'm struggling with the problem from the title for few days already and I'm pretty frustrated. I have no idea what I'm doing wrong and why my implementation isn't working.
Let me show you what I've got:
Custom AuthenticationProvider:
#Component
public class AuthProvider implements AuthenticationProvider {
private Logger logger = LoggerFactory.getLogger(AuthProvider.class);
public AuthProvider() {
logger.info("Building...");
}
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
logger.info("Authenticate...");
return null;
}
public boolean supports(Class<?> authentication) {
logger.info("Supports...");
return true;
}
}
WebSecurity config:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthProvider authProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().authenticated();
}
}
As you can see I've added loggers into the AuthenticationProvider but not any of them is getting called.
What I've tried:
adding #Autowired to configure where the AuthenticationManagerBuilder is
adding #EnableGlobalMethodSecurity(prePostEnabled=true) to the class
adding custom AuthenticationProvider directly to HttpSecurity
How I've tested it:
debugging via IntelliJ - no results, no breakpoint is getting called.
running the app and sending a request - also no results, no logs, nothing.
Please guys help me somehow. I'm outta energy. I hate wasting so much time on things that should just work :(
Probably you missed the following method in your WebSecurityConfigurerAdapter:
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
The same happened to me.
Using the isAssignableFrom() method instead of instead of == or equals we get a true, then the flow would pass through authenticate()
override fun supports(authentication: Class<*>): Boolean {
return UsernamePasswordAuthenticationToken::class.java.isAssignableFrom(authentication)
}
GL
Source

Spring Boot, filter is not getting restricted to specific url

In the spring-boot app, I have created few API calls. I want to add a filter only for few urls. The security config is as follows:
#Configuration
#EnableWebMvc
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.addFilterBefore(authenticationFilter(), BasicAuthenticationFilter.class)
.authorizeRequests().anyRequest().denyAll();
http.authorizeRequests().antMatchers("/api/user").permitAll();
}
AuthenticationFilter authenticationFilter() throws Exception
{
AuthenticationFilter filter = new AuthenticationFilter();
return filter;
}
}
I don't want filter to be applied for any api call except /api/user , so I denied for all urls and permitted for /api/user.
AuthorizationFilter class is as follows:
public class AuthenticationFilter extends OncePerRequestFilter
{
public AuthenticationFilter()
{
super();
}
#Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException
{
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()){
String headerName = headerNames.nextElement();
System.out.println("headerName " + headerName);
System.out.println("headerVal " + request.getHeader(headerName));
}
chain.doFilter(request,response);
}
}
This just prints all header information. Currently it is printing header information on all api calls but I want this to be printed only in case of /api/user and not on any other api call. Please suggest what changes shall I made?
Got a working solution
#Configuration
#EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**");
// add security constraints for /api/... here
}
/* rest of config */
}
How to ignore Spring Security config for every thing except a pattern

Spring Security method level security with Java config + REST Authentification?

I'm trying to add an authentification to my RestController, but i can't find any good documentation or any sample with Java Configuration.
I tried this but it doesn't work (i can access to all request without login)
My controller is annotated with #PreAuthorize
#RestController
#RequestMapping("/api/hello")
public class HelloController {
#RequestMapping(value = "/say", method = RequestMethod.GET)
public String sayHello() {
return "hello";
}
#PreAuthorize("hasRole('ROLE_USER')")
#RequestMapping(value = "/say/user", method = RequestMethod.GET)
public String sayHelloWithUserProtection(){
return "Hello USER";
}
#PreAuthorize("hasRole('ROLE_ADMIN')")
#RequestMapping(value = "/say/admin", method = RequestMethod.GET)
public String sayHelloWithAdminrProtection(){
return "Hello ADMIN";
}
}
SecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ComponentScan(basePackages = {"com.test.server.security"})
public class SecurityConfig {
#Autowired
public void configureAuthentification(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("admin").roles("USER","ADMIN");
}
#Configuration
public static class ApiWebConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.formLogin();
}
}
}
SecurityWebApplicationInitializer
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
How can i make it work?
And there is any good tutorial to make a REST token based (token which save session key and others custom values) authentification saved in database with JPA (or JDBC ?) with Java configuration ?
Drop the formLogin(). You need to maintain the mindset that REST is supposed to be stateless. Logging in with a form this way, is not purely REST.
You can create a fine masked filter with Spring security chains like this (randomly just added stuff to create a more complete. Spring Security works by filters, which means that you need to create an actual filter before it kicks in. Specifically you need to authorize requests before matching them to paths.
http.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/say/user/").hasRole("USER")
.antMatchers("/say/admin").hasRole("ADMIN")
.anyRequest().authenticated();
The code above should be self explaining. If not, I will try to elaborate on it.
As for token based login, this is a good idea, but you should not roll your own.
Spring has great Oauth support and getting started securing your REST API with it is awesome.
This tutorial explains it in great detail and should help you further in building better API's as well.
http://spring.io/guides/tutorials/bookmarks/
Also make sure you have a look at Fowler's writings on REST here
http://martinfowler.com/articles/richardsonMaturityModel.html
I forgot to put my WebApplicationInitialisation in the question.
My error was i put SecurityConfig in getRootConfigClasses() instead of getServletConfigClasses().
Now WebApplicationInitialisation looks like this and it work great !
public class WebApplicationInitialisation extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{RootConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebMvcConfig.class, SecurityConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
}
}

Categories