I know that securing REST API is widely commented topic but I'm not able to create a small prototype that meets my criteria (and I need to confirm that these criteria are realistic). There are so many options how to secure resources and how work with Spring security, I need to clarify if my needs are realistic.
My requirements
- Token based authenticator - users will provide its credentials and get unique and time limited access token. I would like to manage token creation, checking validity, expiration in my own implementation.
- Some REST resources will be public - no need to authenticate at all,
- Some resources will be accessible only for users with administrator rights,
- Other resource will be accessible after authorization for all users.
- I don't want to use Basic authentication
- Java code configuration (not XML)
Current status
My REST API works very well, but now I need to secure it. When I was looking for a solution I created a javax.servlet.Filter
filter:
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
String accessToken = request.getHeader(AUTHORIZATION_TOKEN);
Account account = accountDao.find(accessToken);
if (account == null) {
throw new UnauthorizedException();
}
chain.doFilter(req, res);
}
But this solution with javax.servlet.filters
doesn't work as I need because there is an issue with exception handling via @ControllerAdvice
with Spring servlet dispatcher
.
What I need
I would like to know if these criteria are realistic and get any help, how to start securing REST API with Spring Security. I read many tutorials (e.g. Spring Data REST + Spring Security) but all work in very basic configuration - users with their credentials are stored in memory in configuration and I need to work with DBMS and create own authenticator.
Please give me some ideas how to start.
Token based authentication - users will provide its credentials and get
unique and time limited access token. I would like to manage token
creation, checking validity, expiration in my own implementation.
Actually, use Filter for token Auth - best way in this case
Eventually, you can create CRUD via Spring Data for managing Token's properties like to expire, etc.
Here is my token filter:
http://pastebin.com/13WWpLq2
And Token Service Implementation
http://pastebin.com/dUYM555E
Some REST resources will be public - no need to authenticate at all
It's not a problem, you can manage your resources via Spring security config like this: .antMatchers("/rest/blabla/**").permitAll()
Some resources will be accessible only for users with administrator rights,
Take a look at @Secured
annotation to class. Example:
@Controller
@RequestMapping(value = "/adminservice")
@Secured("ROLE_ADMIN")
public class AdminServiceController {
The other resource will be accessible after authorization for all users.
Back to Spring Security configure, you can configure your url like this:
http
.authorizeRequests()
.antMatchers("/openforall/**").permitAll()
.antMatchers("/alsoopen/**").permitAll()
.anyRequest().authenticated()
I don't want to use Basic authentication
Yep, via token filter, your users will be authenticated.
Java code configuration (not XML)
Back to the words above, look at @EnableWebSecurity
.
Your class will be:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {}
You have to override the configure method. Code below, just for example, how to configure matchers. It's from another project.
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/assets/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.usernameParameter("j_username")
.passwordParameter("j_password")
.loginPage("/login")
.defaultSuccessUrl("/", true)
.successHandler(customAuthenticationSuccessHandler)
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.invalidateHttpSession(true)
.logoutSuccessUrl("/")
.deleteCookies("JSESSIONID")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.and()
.csrf();
}
Spring security also very useful for providing authentication and authorization to the REST URLs. We no need to specify any custom implementations.
First, you need to specify the entry-point-ref to restAuthenticationEntryPoint in your security configuration as below.
<security:http pattern="/api/**" entry-point-ref="restAuthenticationEntryPoint" use-expressions="true" auto-config="true" create-session="stateless" >
<security:intercept-url pattern="/api/userList" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/api/managerList" access="hasRole('ROLE_ADMIN')"/>
<security:custom-filter ref="preAuthFilter" position="PRE_AUTH_FILTER"/>
</security:http>
Implementation for the restAuthenticationEntryPoint might be as below.
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException ) throws IOException {
response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" );
}
}
After this you need to specify RequestHeaderAuthenticationFilter. It contains the RequestHeader key. This is basically used for identifying the user`s authentication. Generally RequestHeader carries this information while making the REST calls.
For example consider below code
<bean id="preAuthFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="Authorization"/>
<property name="authenticationManager" ref="authenticationManager" />
</bean>
Here,
<property name="principalRequestHeader" value="Authorization"/>
"Authorization" is the the key presented the incoming request. It holds the required user`s authentication information.
Also you need to configure the PreAuthenticatedAuthenticationProvider to fulfill our requirement.
<bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="authenticationService"/>
</bean>
</property>
</bean>
This code will work for securing the REST urls by means of Authentication and authorization without any custom implementations.
For Complete code please find the below link:
https://github.com/srinivas1918/spring-rest-security
I searched this long time too.I am working on a similar project.I found out Spring has a module to implement session via redis. It looks easy and useful.
I will add to my project too. Can be helpful:
http://docs.spring.io/spring-session/docs/1.2.1.BUILD-SNAPSHOT/reference/html5/guides/rest.html
To validate REST API there are 2 ways
1 - Basic authentication using default username and password set up in application.properties file
Basic Authentication
2 - Authenticate using database (userDetailsService) with the actual username and password
Advanced Authentication