【问题标题】:SpringBoot different auths (MS AD & JWT) to one ControllerSpringBoot 不同的身份验证(MS AD 和 JWT)到一个控制器
【发布时间】:2020-11-16 18:59:17
【问题描述】:

我尝试在 Spring Boot 上为我的移动应用实现小型 API 网关。

在我的架构中,我将 MS Active Directory Server 用于公司的身份验证人员,并且将来将为客户公司发送短信验证代码以发送 JWT。

我不使用层 DAO、UsersRepository 和 DB 连接。

所有通过 RestTemplate 从服务层发送到我们内部 CRM 系统的 HTTP 请求。

我实现LDAP AD auth很简单HttpBasic配置如下:

@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig  extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors().and().csrf()
                .disable()
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/api/v1/send/**").permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .httpBasic();

    }
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {

        ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider = new
                ActiveDirectoryLdapAuthenticationProvider("mydomain.com", "ldap://192.168.0.100:389/");

        activeDirectoryLdapAuthenticationProvider.setConvertSubErrorCodesToExceptions(true);
        activeDirectoryLdapAuthenticationProvider.setUseAuthenticationRequestCredentials(true);
        activeDirectoryLdapAuthenticationProvider.setSearchFilter("(&(objectClass=user)(userPrincipalName={0})(memberOf=CN=mobileaccess,OU=User Groups,OU=DomainAccountsUsers,DC=MYDOMAIN,DC=COM))");
        auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider);
        auth.eraseCredentials(true);
    }
}

例如,我有两个 RestController V1 和 V2:

@RequestMapping("api/v1")
//get token for staff (AD user) HttpBasic auth
@PostMapping("auth/get/stafftoken")
    public ResponseEntity<?> getToken() {
    // some code...
        HttpHeaders tokenHeaders = new HttpHeaders();
        tokenHeaders.setBearerAuth(tokenAuthenticationService.getToken());
            return new ResponseEntity<>(tokenHeaders, HttpStatus.OK);
    }

//get JWT if code from sms == code in my CRM-system (for client) not auth - permitAll
@PostMapping("send/clienttoken")
    public @ResponseStatus
    ResponseEntity<?> sendVerifyCode(@RequestParam("verifycode") String verifycode) {
    // some code...
        HttpHeaders tokenHeaders = new HttpHeaders();
        tokenHeaders.setBearerAuth(tokenAuthenticationService.getToken());
            return new ResponseEntity<>(tokenHeaders, HttpStatus.OK);
    }

@RequestMapping("api/v2")
@GetMapping("get/contract/{number:[0-9]{6}")
    public Contract getContract(@PathVariable String number) {
        return contractsService.getContract(number);
    }

如何使用 JWT 令牌(客户端和员工)实现对 Controller APIv2 的 Bearer Auth 请求?

我认为这是通过过滤器链实现的?

【问题讨论】:

    标签: spring-boot spring-security spring-security-ldap


    【解决方案1】:

    各位

    如果您在我的示例中实现多重身份验证,首先为构建令牌和验证用户 JWT 创建实用程序类。这是标准代码,例如:

    public static String createUserToken(Authentication authentication) {
        return Jwts.builder()
                .setSubject(authentication.getName())
                .claim(authentication.getAuthorities())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SIGN_KEY)
                .compact();
    

    }

    public static Authentication getAuthentication(HttpServletRequest request) {
        String token = extractJwt(request);
        try {
                if (token != null) {
                    Claims claims = Jwts.parser().setSigningKey(SIGN_KEY).parseClaimsJws(token).getBody();
                    String username = Jwts.parser().setSigningKey(SIGN_KEY).parseClaimsJws(token).getBody().getSubject();
                    return username != null ? new UsernamePasswordAuthenticationToken(username, "", Collections.EMPTY_LIST) : null;
                }
            } catch (ExpiredJwtException e) {
    
            }
        return null;
    }
    

    你应该创建两个过滤器:

    1. Lo​​ginAuthentificationFilter 扩展了 BasicAuthenticationFilter
    2. JwtAuthenticationFilter 扩展了 GenericFilterBean

    下面的代码示例

    public class LoginAuthentificationFilter extends BasicAuthenticationFilter {
    public LoginAuthentificationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        super.doFilterInternal(request, response, chain);
    }
    

    }

    public class JwtAuthenticationFilter extends GenericFilterBean {
    private RequestMatcher requestMatcher;
    public JwtAuthenticationFilter(String path) {
        this.requestMatcher = new AntPathRequestMatcher(path);
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if (!requiresAuthentication((HttpServletRequest) servletRequest)) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        Authentication authentication = JwtUtils.getAuthentication((HttpServletRequest) servletRequest);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        filterChain.doFilter(servletRequest, servletResponse);
    }
    private boolean requiresAuthentication(HttpServletRequest request) {
        return requestMatcher.matches(request);
    }
    

    }

    最后 设置 WebSecurityConfigurerAdapter

    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors().and().csrf().disable()
                .exceptionHandling().authenticationEntryPoint(new JwtAuthenticationEntryPoint())
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/api/v1/noauth_endpoints").permitAll()
                .anyRequest()
                .authenticated()
                .and()
               .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .addFilterAt(jwtFilter(), BasicAuthenticationFilter.class)
                .addFilter(loginFilter());
        http.headers().cacheControl();
    }
    

    豆子

    @Bean
    public LoginAuthentificationFilter loginFilter() {
        return new LoginAuthentificationFilter(authenticationManager());
    }
    
    @Bean
    public JwtAuthenticationFilter jwtFilter() {
        return new JwtAuthenticationFilter("/api/v2/**");
    }
    
    @Bean
    @Override
    public AuthenticationManager authenticationManager() {
        return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
    }
    
    @Bean
    public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
        ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("bzaimy.com", "ldap://192.168.0.100:389/");
        provider.setSearchFilter("(&(objectClass=user)(userPrincipalName={0})(memberOf=CN=mobileaccess,OU=User Groups,OU=DomainAccountsUsers,DC=MYDOMAIN,DC=COM))");
        provider.setConvertSubErrorCodesToExceptions(true);
        provider.setUseAuthenticationRequestCredentials(true);
        return provider;
    }
    

    【讨论】:

      猜你喜欢
      • 2022-07-22
      • 2020-06-04
      • 2022-07-14
      • 2022-01-20
      • 2017-08-26
      • 2012-12-23
      • 2021-11-27
      • 2017-09-09
      • 2020-01-25
      相关资源
      最近更新 更多