Spring Security with REST API

I am trying to create an application that will mainly access the REST API using Spring, and am trying to configure security. Trying to present the actual structure of the application using this picture: enter image description here

  • The request can come from any platform at "abc.com/rest_api/"
  • The request will be sent to point 3 or point 5. If the user is already authenticated by username and password, the request will be checked against Token else, which will be redirected to the database.
  • If the username and password must be authenticated by the database, then a token will be issued and sent back in response.
  • After that, only token-based authentication will work.

, , , - .

@Configuration
@EnableWebSecurity
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled=true)
public class UserDetailsSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private NSecurityContextHolder securityContextHolder;

    @Autowired
    private NHttpServletRequestBinder<Authentication> authenticationBinder;

    public static final String DEF_USERS_BY_USERNAME_QUERY
            = "SELECT user ";


public static final String GROUPS_BY_USERNAME_QUERY =
        "SELECT groups by user";
public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY =
        "SELECT  authorities";


@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        auth.jdbcAuthentication().dataSource(getDataSourceFromJndi())
                .usersByUsernameQuery(DEF_USERS_BY_USERNAME_QUERY).
                authoritiesByUsernameQuery(DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY).
                groupAuthoritiesByUsername(GROUPS_BY_USERNAME_QUERY);


    }
      private DataSource getDataSourceFromJndi() {
        try {


             DataSource dataSource = (DataSource) new InitialContext().lookup("DS");
            return dataSource;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

      @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          auth.jdbcAuthentication().dataSource(getDataSourceFromJndi())
                .usersByUsernameQuery(DEF_USERS_BY_USERNAME_QUERY).
                authoritiesByUsernameQuery(DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY).
                groupAuthoritiesByUsername(GROUPS_BY_USERNAME_QUERY);

    }


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


                // The http.formLogin().defaultSuccessUrl("/path/") method is required when using stateless Spring Security
        // because the session cannot be used to redirect to the page that was requested while signed out. Unfortunately
        // using this configuration method will cause our custom success handler (below) to be overridden with the
        // default success handler. So to replicate the defaultSuccessUrl("/path/") configuration we will instead
        // correctly configure and delegate to the default success handler.
        final SimpleUrlAuthenticationSuccessHandler delegate = new SimpleUrlAuthenticationSuccessHandler();
        delegate.setDefaultTargetUrl("/api/");
          // Make Spring Security stateless. This means no session will be created by Spring Security, nor will it use any
        // previously existing session.
        http.sessionManagement().sessionCreationPolicy(STATELESS);
        // Disable the CSRF prevention because it requires the session, which of course is not available in a
        // stateless application. It also greatly complicates the requirements for the sign in POST request.
        http.csrf().disable();
        // Viewing any page requires authentication.
        http.authorizeRequests().anyRequest().authenticated();
        http
          .formLogin().loginPage("http://localhost/web/ui/#access/signin")
          .permitAll()
            // Override the sign in success handler with our stateless implementation. This will update the response
            // with any headers and cookies that are required for subsequent authenticated requests.
            .successHandler(new NStatelessAuthenticationSuccessHandler(authenticationBinder, delegate));
        http.logout().logoutUrl("http://localhost/web/ui/#access/signin").logoutSuccessUrl("http://localhost/web/ui/#access/signin");
        // Add our stateless authentication filter before the default sign in filter. The default sign in filter is
        // still used for the initial sign in, but if a user is authenticated we need to acknowledge this before it is
        // reached.
        http.addFilterBefore(
            new StatelessAuthenticationFilter(authenticationBinder, securityContextHolder),
            UsernamePasswordAuthenticationFilter.class
        );

} 

}

Binder, TokenBased UserNameBased.

TokenBased:

@Component
public class NXAuthTokenHttpServletRequestBinder implements NHttpServletRequestBinder<String> {

    private static final String X_AUTH_TOKEN = "X-AUTH-TOKEN";
    private final NTokenFactory tokenFactory;

    @Autowired
    public NXAuthTokenHttpServletRequestBinder(NTokenFactory tokenFactory) {
        this.tokenFactory = tokenFactory;
    }

    @Override
    public void add(HttpServletResponse response, String username) {

        final String token = tokenFactory.create(username);

        response.addHeader(X_AUTH_TOKEN, token);
        response.addCookie(new Cookie(X_AUTH_TOKEN, token));
    }

    @Override
    public String retrieve(HttpServletRequest request) {

        final String cookieToken = findToken(request);

        if (cookieToken != null) {
            return tokenFactory.parseUsername(cookieToken);
        }

        return null;
    }

    private static String findToken(HttpServletRequest request) {
        Enumeration<String> it = request.getHeaderNames();
        while(it.hasMoreElements()){
            System.out.println(it.nextElement());
        }
        final String headerToken = request.getHeader(X_AUTH_TOKEN);

        if (headerToken != null) {
            return headerToken;
        }

        final Cookie[] cookies = request.getCookies();

        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (X_AUTH_TOKEN.equals(cookie.getName())) {
                    return cookie.getValue();
                }
            }
        }

        return null;
    }
}

UserBased:

@Component
@Primary
public class NUserAuthenticationFactory implements NHttpServletRequestBinder<Authentication> {

    private final NHttpServletRequestBinder<String> httpServletRequestBinder;

    @Autowired

    public NUserAuthenticationFactory(NHttpServletRequestBinder<String> httpServletRequestBinder) {
        this.httpServletRequestBinder = httpServletRequestBinder;
    }

    @Override
    public void add(HttpServletResponse response, Authentication authentication) {
        httpServletRequestBinder.add(response, authentication.getName());
    }

    @Override
    public UserAuthentication retrieve(HttpServletRequest request) {

        final String username = httpServletRequestBinder.retrieve(request);

        if (username != null) {

            return new UserAuthentication(new CustomJDBCDaoImpl().loadUserByUsername(username));
        }

        return null;
    }
}

, , UserBased Authentication, . , , . .

Logs:

:/ 1 12 ; : "WebAsyncManagerIntegrationFilter" :/ 2 12 ; : "SecurityContextPersistenceFilter" :/ 3 12 ; firing Filter: "HeaderWriterFilter" Fine:
HSTS, Matcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@a837508 :/ 4 12 ; : "LogoutFilter" Fine: : '/'; http://localhost/web/ui/#access/signin ':/ 5 12 ; : "StatelessAuthenticationFilter" Fine:/ 6 12 ; : "UsernamePasswordAuthenticationFilter" Fine: "GET/" match 'POST http://localhost/web/ui/#access/signin :/at 7 12 ; : "RequestCacheAwareFilter" Fine:/ 8 12 ; firing Filter: 'SecurityContextHolderAwareRequestFilter' :/ 9 12 ; : " " : SecurityContextHolder : "Org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: : anonymousUser; : []; : ; : org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; : ROLE_ANONYMOUS 'Fine:/ 10 12 ; firing Filter:" SessionManagementFilter" Fine: Requested ID 3e2c15a2a427bf47e51496d2a186 . :/at 11 12 ; : 'ExceptionTranslationFilter' :/ 12 12 ; firing Filter: 'FilterSecurityInterceptor' Fine: : FilterInvocation: URL:/; : [authenticated] Fine: : org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: : anonymousUser; : []; : ; : org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; : ROLE_ANONYMOUS Fine: : org.springframework.security.web.access.expression.WebExpressionVoter@2ac71565, return: -1 Fine: ( ); org.springframework.security.access.AccessDeniedException:

+4
1

...

  • spring security UsernamePasswordAuthenticationFilter .
  • AuthenticationProvider .
  • UsernamePasswordAuthenticationFilter,

    http.addFilterBefore(CustomTokenBasedAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

  • AuthenticationProvider AuthenticationManager

  • !!!

. - API- - , oauth1a, oauth2.0 .. spring oauth1a oauth2.0.

+2

Source: https://habr.com/ru/post/1613059/


All Articles