vibetribe93
vibetribe93

Reputation: 267

Spring token security with credential security (springboot)

I have a question regarding security implementation on my server. I am making a SpringBoot application which has a control panel like website on it, where 1 single admin inputs needed data and i have managed to secure that part fine like this :

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {




  @Override
  protected void configure(HttpSecurity http) throws Exception {


      http.antMatcher("/*").authorizeRequests().anyRequest().hasRole("ADMIN")
        .and().formLogin().loginPage("/login.jsp")
        .failureUrl("/login.jsp?error=1").loginProcessingUrl("/login")
        .permitAll().and().logout()
        .logoutSuccessUrl("/login.jsp");

  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    // Create a default account
    auth.inMemoryAuthentication()
        .withUser("admin")
        .password("admin")
        .roles("ADMIN");
  }

Every website url is on /*, and that works fine. The next thing i need to do is to retrieve data from my mobile app and it needs to be secure. urls that the app should use is /rest/**. I have a Student class that stores email(username) and password that is created by that admin on web site. As far as i've read i need token implementation.

How can I implement token authentication?

Upvotes: 4

Views: 1961

Answers (1)

Jasper Blues
Jasper Blues

Reputation: 28786

To implement token based authentication for a mobile app, with Spring Boot and Spring Security.

Create a TokenAuthenticationFilter

public class TokenAuthenticationFilter extends GenericFilterBean {

    private AuthenticationManager authenticationManager;

    public TokenAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public void doFilter(ServletRequest request,
                                             ServletResponse response,
                                             FilterChain chain) throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        String apiKey = httpRequest.getHeader("API-Key");
        String token = httpRequest.getHeader("Access-Token");

        try {
            if (!StringUtils.isEmpty(apiKey)) {
                processTokenAuthentication(apiKey);
            }
            chain.doFilter(request, response);
        } catch (InternalAuthenticationServiceException internalAuthenticationServiceException)
        {
            SecurityContextHolder.clearContext();
            logger.error("Internal authentication service exception", internalAuthenticationServiceException);
            httpResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
            catch(AuthenticationException authenticationException)
        {
            SecurityContextHolder.clearContext();
            httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, authenticationException.getMessage());
        }
    }

    private void processTokenAuthentication(String apiKey) {
        SessionCredentials authCredentials = new SessionCredentials(apiKey);
        Authentication requestAuthentication = new PreAuthenticatedAuthenticationToken(authCredentials, authCredentials);
        Authentication resultOfAuthentication = tryToAuthenticate(requestAuthentication);
        SecurityContextHolder.getContext().setAuthentication(resultOfAuthentication);
    }

    private Authentication tryToAuthenticate(Authentication requestAuthentication) {
        Authentication responseAuthentication = authenticationManager.authenticate(requestAuthentication);
        if (responseAuthentication == null || !responseAuthentication.isAuthenticated()) {
            throw new InternalAuthenticationServiceException("Unable to authenticate Domain User for provided credentials");
        }
        return responseAuthentication;
    }
}

public class TokenAuthenticationProvider implements AuthenticationProvider {

    private String apiKey;

    public TokenAuthenticationProvider(String apiKey) {
        this.apiKey = apiKey;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        SessionCredentials credentials = (SessionCredentials) authentication.getCredentials();
        if (credentials != null && credentials.apiKey.equals(this.apiKey)) {

            //Also evaluate the token here

            Authentication newAuthentication = new PreAuthenticatedAuthenticationToken(apiKey, credentials);
            newAuthentication.setAuthenticated(true);
            return newAuthentication;
        }
        throw new BadCredentialsException("Bad credentials given.");
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return aClass.equals(PreAuthenticatedAuthenticationToken.class);
    }
}

Create Session Credentials Holder

public class SessionCredentials {

    String apiKey;
    String accessToken;

    public SessionCredentials(String apiKey, String accessToken) {
        this.apiKey = apiKey;
        this.accessToken = accessToken;
    }

    public String getApiKey() {
        return apiKey;
    }

    public String getAccessToken() {
        return accessToken;
    }
}

Finally Register These in your Security Config

//Leave whatever you had here
@Override
public void configure(HttpSecurity http) throws Exception {
    http.addFilterBefore(new TokenAuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class);

    String contentPathDir = String.format("/%s/**", contentPath);

    http.csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and().authorizeRequests()
            .antMatchers("/authorization/**", "/public/**", "/management/**", "/health/**", contentPathDir).permitAll()
            .antMatchers("/**").authenticated();
}




//Add these two below. 
@Override
public void configure(AuthenticationManagerBuilder auth) {
    auth.authenticationProvider(apiKeyAuthenticationProvider());
}

@Bean
public TokenAuthenticationProvider apiKeyAuthenticationProvider() {
    return new TokenAuthenticationProvider(apiKey);
}

Upvotes: 4

Related Questions