跨多个WebSecurityConfigurerAdapters共享的SecurityContextHolder

时间:2019-06-11 19:26:10

标签: spring spring-boot spring-security

上下文:我正在编写一个Web应用程序,旨在提供两个不同的API,每个API都有自己的身份验证过滤器。过滤器都处理和验证JWT令牌,但是令牌本身包含不同的有效负载,并且来自不同的身份验证源。另外,即使令牌不同,并且令牌来自不同的来源,它们也共享相同的登录凭据,并使用相同的密钥进行保护。

即:您可以使用相同的凭据访问这些身份验证URL中的任意一个,并获得具有完全不同的有效负载的不同的JWT令牌。

/ auth / auth1 /登录 / auth / auth2 /登录

问题:我遇到的问题是,如果我对其中一个进行身份验证,则可以访问另一个。即使没有提供令牌。

意思是如果我转到/ requests / something,使用来自身份验证提供程序1的承载令牌进行身份验证,然后转到/ ims / oneroster / v1p1 / somethingElse(无需传递来自身份验证提供程序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 :(得分:0)

阅读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);
        }
    }
}