The simpliest, yet effective way to secure Spring REST API is to use Basic Auth. I’m going to show how to do it with Spring Security.
Note
Basic Auth is stateless (no need to manage sessions) and simple to implement. However it should be used with https only if outside of a trusted network. Also be aware that there are issues with Basic Auth (http://security.stackexchange.com/questions/988/is-basic-auth-secure-if-done-over-https)
Source Code
As usual, get it from my GitHub: https://github.com/yacekmm/looksok/tree/HttpBasicAuthDemo/Spring/SecurityBasicAuth
1. Create a project
Use your favorite tool. I choose Spring Boot Initializr in IntelliJ. Add web and security starter poms:
Note that right after you add the security starter pom to your classpath, the auto configuration kicks in and your app is secured by default (the default password is logged on the application startup). I will override that default configuration and customize it later on.
2. Create Controller with urls to be secured
The most straight forward one as possible like mine below:
@RestController @RequestMapping("/api") public class SecuredController { @RequestMapping(value = "/secured", method = GET, produces = TEXT_PLAIN_VALUE) public String securedGet(){ return "You have access to secured API!"; } @RequestMapping(value = "/admin", method = GET, produces = TEXT_PLAIN_VALUE) public String adminGet(){ return "Welcome, Admin"; } @RequestMapping(value = "/open", method = GET, produces = TEXT_PLAIN_VALUE) public String openedGet(){ return "this API does not require authentication"; } }
It contains 3 endpoints. Each of them I will secure to be accessible on various levels.
3. Create Basic Auth security configuration for selected endpoints
As in one of my previous tutorials (Spring Security Tutorial: Authorization and user roles), you need to extend the WebSecurityConfigurerAdapter:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { ... }
and configure the HttpSecurity in there:
@Override protected void configure(HttpSecurity http) throws Exception { http .httpBasic() .and() .authorizeRequests() .antMatchers("/api/secured").authenticated() .antMatchers("/api/admin").hasRole("ADMIN") .antMatchers("/api/open").permitAll() .anyRequest().authenticated(); }
What just happened here?
- first of all with httpBasic() you say that BasicAuth will be in use
- then you say that want to authorizeRequests() so that:
- url matching “/api/secured” is accessible for all authenticated users
- url matching “/api/admin” is accessible for ADMIN users only
- url matching “/api/open” is accessible with no authentication required
- any other request is accessible for all authenticated() users
4. Create security configuration: usernames and passwords
Here I create two user accounts with USER and ADMIN role. That method is also in my WebsecurityConfig class. Using those accounts you can log in to the application:
@Autowired protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("admin").password("superhard").roles("ADMIN") .and() .withUser("user").password("easyone").roles("USER"); }
Normaly you would use the AuthenticationProvider, probably connected with your database or LDAP. This is separate big subject not to be cevered in this article.
5. Go ahead and try it!
Run the app and:
- head over to url 127.0.0.1:8080/api/open – you should access it without logging in
- head over http://127.0.0.1:8080/api/secured – the browser will ask you for username and password: type ‘user’ and ‘easyone’. Then you should access the site
- head over to urlĀ http://127.0.0.1:8080/api/admin – you will get 403 Forbidden, since browser cached your auth data and reused it. Only admin is permitted to access that url.
- To log in as another user you need to restart your browser and open the url once again. You can also use private browsing.