当前位置: 首页 > 知识库问答 >
问题:

多个安全适配器-addFilter在没有预期的工作

王曜文
2023-03-14

我想要APIMVC的配置。对API的授权是JWT,而对MVC的授权是x509客户机证书(Separatley这两种配置都运行良好)。

预期行为:

  1. 请求 /v2/api/**将使用addFilterBy中给出的过滤器进行过滤,其中jwt被验证并创建上下文
  2. 所有其他请求都将使用ssl客户端证书验证。

它现在的工作方式——任何请求都会在之前触发AddFilterb,并因缺少jwt令牌而被拒绝。

我的配置类:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class X509AuthenticationServer extends WebSecurityConfigurerAdapter
{

    @Configuration
    @Order(1)
    public static class JwtAuth extends WebSecurityConfigurerAdapter
    {

        @Autowired
        private JwtRequestFilter jwtRequestFilter;

        @Override
        protected void configure(HttpSecurity http) throws Exception
        {
            http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
                .antMatcher("/v2/api/**").authorizeRequests()
                .antMatchers("/v2/api/**").authenticated()
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().csrf().disable();
        }
    }

    @Configuration
    @Order(4)
    public class x509Authenticator extends X509AuthenticationServer
    {

        @Override
        protected void configure(HttpSecurity http) throws Exception
        {
            http.authorizeRequests().anyRequest().authenticated()
                .and().x509().subjectPrincipalRegex("CN=(.*?)(?:,|$)")
                .userDetailsService(userDetailsService())
                .and().exceptionHandling().accessDeniedPage("/forbidden");

        }

        @Bean
        public UserDetailsService userDetailsService() 
        {
            return new UserDetailsService() 
            {
                @Override
                public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 
                {
                    return new User(username, "", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
                }
            }
        }
    }
}

共有1个答案

颛孙建业
2023-03-14

请注意:您提供的信息/代码不够。由于您没有提供X509AuthenticationServerJwtRequestFilter的代码,我无法判断到底是什么错误。

假设这是你的紧急要求,试着解释所有的可能性。

它现在的工作方式——任何请求都会在之前触发AddFilterb,并因缺少jwt令牌而被拒绝。

是的,任何请求都会通过过滤器。在过滤器中,只有一件事需要注意:检查标题,如果存在,则继续进行JWT身份验证,否则跳过JWT身份验证(如果阻止,请检查我的过滤器类代码)

当您的Spring Security配置为x509时,身份验证对象将由证书主体使用regex来设置。x509()。subjectPrinialRegex("CN=(. *?)(?:,|$)")

>

对于由服务器证书组成的JWT请求,将从服务器证书主题设置主体。现在通过从JWT令牌获取用户名并授予这些用户所需的权限来覆盖主体

Subject: EMAILADDRESS=nlpraveennl@gmail.com, CN=Praveen, OU=Answer, O=StackOverflow, L=BENGALURU, ST=KA, C=IN
[
  Version: V1
  Subject: EMAILADDRESS=vedanta@gmail.com, CN=vedanta, OU=some unit, O=some comapany, L=San diego, ST=CA, C=US
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

  Key:  Sun RSA public key, 4096 bits
  modulus: 817322829240490927539679977649457347113079769252522527800627995161015683461174014845370356721299781132807751160825513223084246773438383264091041820195243162665509891042158368380019167357193944418022840037166629645037683573001749034046239097004884878919482801656531508586406926436161114752390080130151471441072696492233900694526232243678291240183028778626646828176141484812179759739175161094296327915519918417719601837669497535100237474334129104633049344560273440876841464270970566837970617275659841740418106346304917775719024288908219661239022501256531355020518204348890248534705892780384737192506755315365883201062844995260628679776392945804218936346471987181753817158168697446990117525268883019172878686864407803654159029932574084328051385395658876802073285425506794524283532870369321962543974623256690683454729681498079311854836252232196330777070325603711372859419866719120909302184446204160932841096818392866335724975192880990513954025684813917171551040959488266330875554906980729293764667616531866907648957912796417658137011975409928985274876102141544954375822680116494691313951508398067207453068155470604238471192479465455519868221517071480577973522583550201343549400093003081375335896091143428006322327934212827941149315676683
  public exponent: 65537
  Validity: [From: Tue Oct 15 09:04:58 IST 2019,
               To: Fri Oct 09 09:04:58 IST 2020]
  Issuer: EMAILADDRESS=nlpraveennl@gmail.com, CN=Praveen, OU=Answer, O=StackOverflow, L=BENGALURU, ST=KA, C=IN
  SerialNumber: [    baafa655 dd56be52]

]

1.对于v2应用编程接口(JWT):您没有在请求中发送服务器证书。我同意它不需要客户端证书,但是服务器只接受通过SSL连接的通信。

>

  • 您不能通过curl cmd测试它。只有当您的SSL由CA签名时,它才有效。

    你不能在google chrome中测试这一点,因为google chrome还需要CA签署的SSL。

    黑客测试:
    使用服务器证书和安全密钥以及JWT令牌,如下所示

    curl -ik --cert server.crt --key serverPrivateKey.pem -H "Authorization:Bearer jwtToken" "https://localhost:8443/v2/api/yourpath"
    

    它应该起作用。

    2.对于v2以外的应用编程接口(没有JWT):您没有在请求中包括客户端证书。正确的测试方法是。

    curl -ik --cert pavel.crt --key myPrivateKey.pem "https://localhost:8443/v1/hello"
    curl -ik --cert pavel.crt --key myPrivateKey.pem "https://localhost:8443/v1.5/hello"
    

    我已经测试过了,它对我来说没有任何问题。我唯一的东西

    仅供参考,在这里添加代码。

    @Configuration
    @EnableWebSecurity
    public class SpringSecurityConfig 
    {
    
        @Configuration
        @Order(1)
        public static class JwtConfiguration extends WebSecurityConfigurerAdapter
        {
    
            @Autowired
            private JwtAuthenticationTokenFilter jwtRequestFilter;
    
            @Override
            protected void configure(HttpSecurity http) throws Exception
            {
                http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
                    .antMatcher("/v2/**").authorizeRequests()
                    .antMatchers("/v2/**").authenticated()
                    .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and().csrf().disable();
            }
        }
    
        @Configuration
        @Order(4)
        public static class X509Configuration extends WebSecurityConfigurerAdapter
        {
    
            @Override
            protected void configure(HttpSecurity http) throws Exception
            {
                http.authorizeRequests().anyRequest().authenticated()
                    .and().x509().subjectPrincipalRegex("CN=(.*?)(?:,|$)")
                    .userDetailsService(userDetailsService())
                    .and().exceptionHandling().accessDeniedPage("/forbidden");
    
            }
    
            @Bean
            public UserDetailsService userDetailsService() 
            {
                return new UserDetailsService() 
                {
                    @Override
                    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 
                    {
                        System.out.println("[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]");
                        System.out.println(username);
                        System.out.println("[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]");
                        return new User(username, "", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
                    }
                };
            }
        }
    }
    
    @Component
    public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
    {
        @Autowired
        private JwtTokenUtil jwtTokenUtil;
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException
        {
            System.out.println("(((((((((((((((())))))))))))))");
            final String header = request.getHeader("Authorization");
    
            if (header != null && header.startsWith("Bearer ")) 
            {
                String authToken = header.substring(7);
                System.out.println(authToken);
    
                try
                {
                    String username = jwtTokenUtil.getUsernameFromToken(authToken);
                    if (username != null && SecurityContextHolder.getContext().getAuthentication() == null)
                    {
                        // here username should be validated with database and get authorities from database if valid
                        // Say just to hard code sending same username received
                        if (jwtTokenUtil.validateToken(authToken, username))
                        {
                            List<GrantedAuthority> authList = new ArrayList<>();
                            authList.add(new SimpleGrantedAuthority("ROLE_APIUSER"));
    
                            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, null, authList);
                            usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
    
                            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
                        }
                    }
                }
                catch (Exception e)
                {
                    System.out.println("Unable to get JWT Token, possibly expired");
                }
            }
    
            chain.doFilter(request, response);
        }
    }
    
    @Component
    public class JwtTokenUtil implements Serializable
    {
        private static final long   serialVersionUID    = 8544329907338151549L;
        public static final long    JWT_TOKEN_VALIDITY  = 5 * 60 * 60;
        private String              secret              = "my-secret";
    
        public String getUsernameFromToken(String token)
        {
            return getClaimFromToken(token, Claims::getSubject);
        }
    
        public Date getExpirationDateFromToken(String token)
        {
            return getClaimFromToken(token, Claims::getExpiration);
        }
    
        public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver)
        {
            final Claims claims = getAllClaimsFromToken(token);
            return claimsResolver.apply(claims);
        }
    
        private Claims getAllClaimsFromToken(String token)
        {
            return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        }
    
        private Boolean isTokenExpired(String token)
        {
            final Date expiration = getExpirationDateFromToken(token);
            return expiration.before(new Date());
        }
    
        public String generateToken(String username)
        {
            Map<String, Object> claims = new HashMap<>();
            return doGenerateToken(claims, username);
        }
    
        private String doGenerateToken(Map<String, Object> claims, String subject)
        {
            return "Bearer "+Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
                    .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)).signWith(SignatureAlgorithm.HS512, secret).compact();
        }
    
        public Boolean validateToken(String token, String usernameFromToken)
        {
            final String username = getUsernameFromToken(token);
            return (username.equals(usernameFromToken) && !isTokenExpired(token));
        }
    
        //To generate token for testing
        public static void main(String[] args)
        {
            JwtTokenUtil tu = new JwtTokenUtil();
            String s1 = tu.generateToken("hello");
            System.out.println(s1);
            String user = tu.getUsernameFromToken(s1);
            System.out.println(user);
        }
    }
    
    @RestController
    public class HelloController
    {
        //Client certificate
        @RequestMapping(path = "/v1/hello")
        public String helloV1()
        {
    
            return "HELLO Version 1 - "+((User)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
        }
    
        //Client certificate
        @RequestMapping(path = "/v1.5/hello")
        public String helloV1Dot5()
        {
            return "HELLO Version 1.5 - "+((User)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
        }
    
        //Jwt
        @RequestMapping(path = "/v2/hello")
        public String helloV2()
        {
            return "HELLO Version 2 - "+SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        }
    }
    

  •  类似资料:
    • 我重新安排了云firestore的安全规则,以防止在集合中重新创建文档。以下是我使用的规则: 我用这些文档路径和用户凭据在控制台中模拟了这些规则 如果我创建了一个id为09-07-2020的文档,并使用上述路径进行模拟,则会失败。当我删除文件09-07-2020时,上述模拟工作正常。因此,它在规则模拟器中运行良好。 但当我在应用程序中尝试时,每次尝试都会再次创建文档。因此,如果文档内容以前不同,则

    • 我有自定义控制器和spring应用程序,其配置如下: 似乎对于一个一致的用户来说,它工作得很好。但是如果我已经授权(使用oauth2),并且我的会话(或令牌)过期了->spring尝试将我重定向到登录页面。 有趣的是,内置endpoint按预期工作,即使会话过期。

    • 现在,在我的drools项目中,我在单独的DRL文件中有两组规则,它们由议程组分割。对于议程组“preCheck”,我将该议程组中的每个规则的自动聚焦设置为true。例子: 对于另一个议程组-“default规则”-规则没有设置自动焦点属性。示例: 在通过RESTAPI调用规则时,我还试图通过JSON负载将焦点设置为“preCheck”议程组。例子: 然而,在执行规则时,似乎首先要评估“defau

    • 问题内容: 我想交换两个秒- 而在此例,并做到在一个没有线的库函数。 所以我从这里开始: 输出是预期的。到目前为止一切都很好。 接下来是: 输出再次符合预期。到目前为止还不错。 然后最后这个: 在此阶段,输出变为。现在我知道这是由于当时未完成分配而导致的-为什么会发生这种情况?为什么不实际分配给变量,以使其成为最后一次分配? 问题答案: 让我们尝试扩展您的最后一个表达式。 评估为 需要注意的是表达

    • 你知道我错过了什么吗?

    • 基本上,这是注册的用户ID和每个用户订阅的组ID。我想编写一个安全规则,允许只有当用户是当前auth用户时才访问用户配置文件和子集合,根据我对文档的阅读,我认为通配符可以实现这一点... 这样,我就可以很好地读取文档,但是当我尝试读取子集合时,会出现权限错误。我只能通过显式匹配子集合来使它工作... ...所以我的问题是,这两个例子之间有什么区别,我对递归通配符有什么误解?我认为,第一个示例的部分