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)