Общий SecurityContextHolder для нескольких WebSecurityConfigurerAdapters - PullRequest
1 голос
/ 11 июня 2019

Контекст: Я пишу веб-приложение, предназначенное для обслуживания двух разных API, каждый со своим фильтром аутентификации.Оба фильтра обрабатывают и аутентифицируют токены JWT, однако сами токены содержат разные полезные данные и поступают из разных источников аутентификации.Кроме того, несмотря на то, что токены различаются и поступают из разных источников, они имеют одни и те же учетные данные для входа в систему и защищены одним и тем же ключом.

Т.е.: вы можете перейти к любому из этих URL-адресов авторизации., с теми же учетными данными и верните другой токен JWT с совершенно разными полезными нагрузками.

/ auth / auth1 / login / auth / auth2 / login

Проблема: Проблема, с которой я сталкиваюсь, заключается в том, что если я аутентифицируюсь на одном из них, у меня есть доступ к другому.Даже без предоставления токена.

То есть, если я перехожу к / запросы / что-то, аутентифицируюсь с использованием токена-носителя от провайдера аутентификации 1, затем перехожу к / ims / oneroster / v1p1 / кое-что (без передачи другого токенаот провайдера аутентификации 2) Я могу получить доступ к данным, даже если я не проходил аутентификацию, используя фильтр, связанный с этим путем.

На данный момент я знаю единственный способ убедиться, что каждый фильтрЧтобы правильно проверить, является ли токен пользователя действительным, нужно поместить SecurityContextHolder.clearContext (); в верхнюю часть каждого из фильтров doFilterInternal.Однако я почти уверен, что не должен этого делать.

Может кто-нибудь увидеть проблему с тем, что у меня ниже, или дать какие-нибудь рекомендации?

SecurityConfig.class

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
    //https://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html#multiple-httpsecurity

    @Configuration @Order(1)
    public static class XPressWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        private final CacheService cacheService;

        public XPressWebSecurityConfigurationAdapter(CacheService cacheService) {this.cacheService = cacheService;}

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/requests/**")
                    .authorizeRequests().anyRequest().authenticated()
                    .and()
                        .addFilter(new JWTAuthorizationFilter(authenticationManagerBean(), cacheService))
                            //.exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint())
            ;
        }
    }

    @Configuration @Order(2)
    public static class OneRosterWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        private final CacheService cacheService;

        public OneRosterWebSecurityConfigurationAdapter(CacheService cacheService) {this.cacheService = cacheService;}

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/ims/oneroster/v1p1/**")
                    .authorizeRequests().anyRequest().authenticated()
                    .and().addFilter(new OneRosterAuthorizationFilter(authenticationManagerBean(), cacheService))
            ;
        }
    }
}

OneRosterAuthorizationFilter.class

public class OneRosterAuthorizationFilter extends BasicAuthenticationFilter {
    private final CacheService cacheService;

    public OneRosterAuthorizationFilter(AuthenticationManager authManager, CacheService cacheService) {
        super(authManager);
        this.cacheService = cacheService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
        logger.debug("GOING TO ONEROSTER FILTER");

        AuthRequest authRequest = new AuthRequest(req);
        if(authRequest.isAuthEnabled()) {
            if(authRequest.isHeader() || (authRequest.isParameter() && authRequest.isAllowTokenParameter())) {
                UsernamePasswordAuthenticationToken authentication = getAuthentication(req, authRequest);
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        chain.doFilter(req, res);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest req, AuthRequest authRequest) {
        if(StringUtils.isBlank(authRequest.getToken())) {
            return null;  //Token was blank... 403 Forbidden
        }

        DecodedToken decodedToken = TokenDecoder.decodeToken(authRequest.getToken());

        Application application = null;
        if(decodedToken != null) {
            application = new Application(decodedToken.getAppId(), decodedToken.getToken(), cacheService);
        }

        try {
                if(!System.getenv("provider_id").equalsIgnoreCase(decodedToken.getProviderId())) {
                    throw new JWTVerificationException("Provider Ids Don't Match....");
                }

                if(application != null && StringUtils.isNotBlank(application.getApp().getProviderSecret())) {
                JWT.require(Algorithm.HMAC256(application.getApp().getProviderSecret().getBytes()))
                        .withIssuer(PropertiesLoader.getInstance().getProperty("security.auth.jwt.issuer"))
                        .build().verify(authRequest.getToken());
                return new UsernamePasswordAuthenticationToken(application, decodedToken.getToken(), getACLs(application));
            }
        }
        catch (JWTVerificationException exception) {
            //https://medium.com/fullstackblog/spring-security-jwt-token-expired-custom-response-b85437914b81
            req.setAttribute("JWTVerificationException", exception.getMessage());
            return null;
        }
        return null; //DecodedToken or Application was null... 403 Forbidden
    }


    private Collection<GrantedAuthority> getACLs(Application application) {
        Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        application.getPermissions().forEach(pathPermission -> {
            if(pathPermission.getGet()) {
                grantedAuthorities.add(new SimpleGrantedAuthority("get:" + pathPermission.getPath()));
            }
            if(pathPermission.getPost()) {
                grantedAuthorities.add(new SimpleGrantedAuthority("post:" + pathPermission.getPath()));
            }
            if(pathPermission.getPut()) {
                grantedAuthorities.add(new SimpleGrantedAuthority("put:" + pathPermission.getPath()));
            }
            if(pathPermission.getDelete()) {
                grantedAuthorities.add(new SimpleGrantedAuthority("delete:" + pathPermission.getPath()));
            }
        });
        return grantedAuthorities;
    }
}

JWTAuthorizationFilter.class

public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
    private final CacheService cacheService;

    public JWTAuthorizationFilter(AuthenticationManager authManager, CacheService cacheService) {
        super(authManager);
        this.cacheService = cacheService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
        logger.debug("GOING TO JWT FILTER");

        AuthRequest authRequest = new AuthRequest(req);
        if(authRequest.isAuthEnabled()) {
            if(authRequest.isHeader() || (authRequest.isParameter() && authRequest.isAllowTokenParameter())) {
                UsernamePasswordAuthenticationToken authentication = getAuthentication(req, authRequest);
                if(authentication != null) {
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        }
        chain.doFilter(req, res);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest req, AuthRequest authRequest) {
        if(StringUtils.isBlank(authRequest.getToken())) {
            return null;  //Token was blank... 403 Forbidden
        }

        DecodedToken decodedToken = TokenDecoder.decodeToken(authRequest.getToken());

        Application application = null;
        if(decodedToken != null) {
            application = new Application(decodedToken.getApplication_id(), decodedToken.getToken(), cacheService);
        }

        try {
            if(application != null && StringUtils.isNotBlank(application.getApp().getProviderSecret())) {
                JWT.require(Algorithm.HMAC256(application.getApp().getProviderSecret().getBytes()))
                        .withIssuer(PropertiesLoader.getInstance().getProperty("security.auth.jwt.issuer"))
                        .build().verify(authRequest.getToken());
                return new UsernamePasswordAuthenticationToken(application, decodedToken.getToken(), getACLs(application));
            }
        }
        catch (JWTVerificationException exception) {
            //https://medium.com/fullstackblog/spring-security-jwt-token-expired-custom-response-b85437914b81
            req.setAttribute("JWTVerificationException", exception.getMessage());
            return null;
        }
        return null; //DecodedToken or Application was null... 403 Forbidden
    }


    private Collection<GrantedAuthority> getACLs(Application application) {
        Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        application.getPermissions().forEach(pathPermission -> {
            if(pathPermission.getGet()) {
                grantedAuthorities.add(new SimpleGrantedAuthority("get:" + pathPermission.getPath()));
            }
            if(pathPermission.getPost()) {
                grantedAuthorities.add(new SimpleGrantedAuthority("post:" + pathPermission.getPath()));
            }
            if(pathPermission.getPut()) {
                grantedAuthorities.add(new SimpleGrantedAuthority("put:" + pathPermission.getPath()));
            }
            if(pathPermission.getDelete()) {
                grantedAuthorities.add(new SimpleGrantedAuthority("delete:" + pathPermission.getPath()));
            }
        });
        return grantedAuthorities;
    }
}
Note: Application is the class that implements UserDetails

1 Ответ

0 голосов
/ 13 июня 2019

После прочтения комментария от dur, вот решение моей проблемы.Все, что мне нужно было сделать, это добавить:

.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

к каждому из объектов HttpSecurity.

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
    //https://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html#multiple-httpsecurity

    @Configuration @Order(1)
    public static class XPressWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        private final CacheService cacheService;

        public XPressWebSecurityConfigurationAdapter(CacheService cacheService) {this.cacheService = cacheService;}

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //https://auth0.com/blog/implementing-jwt-authentication-on-spring-boot/
            http.antMatcher("/requests/**")
                    .authorizeRequests().anyRequest().authenticated()
                    .and().addFilter(new JWTAuthorizationFilter(authenticationManagerBean(), cacheService))
                        .exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint())
                    .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }
    }

    @Configuration @Order(2)
    public static class OneRosterWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        private final CacheService cacheService;

        public OneRosterWebSecurityConfigurationAdapter(CacheService cacheService) {this.cacheService = cacheService;}

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/ims/oneroster/v1p1/**")
                    .authorizeRequests().anyRequest().authenticated()
                    .and().addFilter(new OneRosterAuthorizationFilter(authenticationManagerBean(), cacheService))
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...