Getting started
If you want security feature in your REST API App, USE Spring security. BETTER THAN PLAIN Interceptor
for WebMVC or WebFilter
for Webflux your spring app.
I'll show you small tutorial with code. Let's get started.
Step 1. AuthenticationToken
Prepare your Authentication Token. If your authentication data is nothing special, just use UsernamePasswordAuthenticationToken
. Are you using JWT? No worries. or, you can implement class with AbstractAuthenticationToken
for identity your authentication token.
Step 2. AuthenticationFilter
When client request your app, You will get authentication infomation by request entities. like header, request body, etc.
How to make about getting authentication infomation from HTTP request? then you need to define a servlet filter. usually implements of GenericFilterBean
or OncePerRequestFilter
. I chose OncePerRequestFilter
because it can implements method with HttpServletRequest
and HttpServletResponse
rather then more abstract interface like ServletRequest
and ServletResponse
.
@Component
public class ApiAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String principal = "Your Principal(ex. User ID) from request";
String credentials = "Your Credentials(ex. User Password) from request";
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(principal , credentials, /* DEFAULT ROLES */);
SecurityContextHolder.getContext().setAuthentication(token);
filterChain.doFilter(request, response);
}
}
Step 3. AuthenticationProvider
Next, You will need verify the authentication infomation. you need implement AuthenticationProvider
for what kind of verify authentication in your app. such as Verfy JWT Signature, get from your DB, etc.
@Component
public class ApiAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Object principal = authentication.getPrincipal();
Object credentials = authentication.getCredentials();
// TODO: Your verify authentication logic from principal and credentials.
if(verified) {
return new UsernamePasswordAuthenticationToken(principal , credentials, /* REAL ROLES FOR authenticated */);
} else return null;
}
@Override
public boolean supports(Class<?> authentication) {
// ... or equals your AuthenticationToken class.
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
Step 4. AuthenticationEntryPoint
If you have plan of REST API, when authentication failed or unauthorized request. You have to implement own class of AuthenticationEntryPoint
, you'll see unwanted unauthorizaed response by Spring Security.
@Component
public class ApiAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
log.error("UnAuthorizaed!!! message : " + e.getMessage());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
try (OutputStream os = response.getOutputStream()) {
// write your own unauthorized response here.
objectMapper.writeValue(os, e);
os.flush();
}
}
}
Step 5. AccessDeniedHandler
also you need implement class of AccessDeniedHandler
for your own response when authentication successful but no have permission roles.
@Component
public class ApiAccessDeniedHandler implements AccessDeniedHandler {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
log.error("Forbidden!!! message : " + e.getMessage());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpStatus.FORBIDDEN.value());
try (OutputStream os = response.getOutputStream()) {
// write your own FORBIDDEN response here.
objectMapper.writeValue(os, e);
os.flush();
}
}
}
Step 6. WebSecurityConfigurerAdapter
At last, configure your app for secure, implement WebSecurityConfigurerAdapter
and import security classes you made.
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
@Import({ ApiAuthenticationFilter.class, ApiAuthenticationProvider.class, ApiAuthenticationEntryPoint.class, ApiAccessDeniedHandler.class})
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final ApiAuthenticationFilter apiAuthenticationFilter;
private final ApiAuthenticationProvider apiAuthenticationProvider;
private final ApiAuthenticationEntryPoint apiAuthenticationEntryPoint;
private final ApiAccessDeniedHandler apiAccessDeniedHandler;
@Autowired
public SecurityConfig() {
// TODO: Autowire these beans...
// or use @RequiredArgsConstructor instead if you are using lombok.
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(apiAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable()
.csrf().disable() // required if rest api.
.cors().and() // reuired if client is browser.
// Required if you want stateless authentication method like JWT.
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(apiAuthenticationEntryPoint)
.accessDeniedHandler(apiAccessDeniedHandler)
.and()
.authorizeRequests()
.antMatchers("/public/**").permitAll() // for public resources
.anyRequest().authenticated()
.expressionHandler(defaultWebSecurityExpressionHandler())
.and()
.addFilterBefore(apiAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.formLogin().disable();
}
// optional: if you want role names without prefix.
private DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler() {
DefaultWebSecurityExpressionHandler result = new DefaultWebSecurityExpressionHandler();
result.setDefaultRolePrefix("");
return result;
}
}
Done. next start your spring boot app and check it works.
Happy Coding!
Top comments (0)