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

为什么在我的自定义身份验证提供程序中不能抛出或处理异常?

韩麒
2023-03-14

我尝试使用JWT来保护基于Spring Security性的资源,以实现以下目标:1。令牌无效或过期,返回401。2、已成功授权,但无权联系某些控制器。然后返回403。现在它出了点问题。当用户无法通过身份验证时,我在自定义的AuthenticationProvider(名为TokenAuthenticationProvider)中抛出BadCredentialsException。但它最终返回403。如何处理异常并返回403 http代码。

我试图实现AuthenticationEntryPoint,但没有成功。处理异常的另一种方法是使用自定义过滤器捕捉异常。但这种方法肯定不起作用,因为即使http响应也不会显示500个BadCredentialsException。因此,一定有一个地方已经捕捉到了这个异常,我无法理解。

令牌认证rovider.class

public class TokenAuthenticationProvider implements AuthenticationProvider {

    UserService userService;

    public TokenAuthenticationProvider(UserService userService) {
        this.userService = userService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        throw new BadCredentialsException("hello");
    }

    @Override
    public boolean supports(Class<?> aClass) {
        System.out.println(aClass);
        TokenAuthenticationProvider.class.isAssignableFrom(aClass);
        return true;
    }
}

网页ecurity.class

@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {

    @Autowired
    UserService userService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .antMatcher("/**")
                .addFilterAfter(new TokenAuthenticationFilter(), BasicAuthenticationFilter.class)
                .authorizeRequests()
                .anyRequest().hasRole("API");
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth){
        auth.authenticationProvider(new TokenAuthenticationProvider(userService));
    }
}

令牌认证ilter.class

public class TokenAuthenticationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        SecurityContextHolder.getContext().setAuthentication(new TokenAuthentication("hello"));
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}

上面的代码进行了简化,我没有遵循正常的流程,而是直接抛出BadCreentalsException,我能做些什么来处理这个Exception并返回401超文本传输协议代码。

共有1个答案

吕宣
2023-03-14

您需要实现两个过滤器来控制生成的JWT。

第一个过滤器是进行身份验证,并在身份验证成功时将JWT发送给客户端。

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManger) {
        this.authenticationManager = authenticationManger;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, 
            HttpServletResponse response) throws AuthenticationException {
        try {
            AuthenticationRequest authRequest = new ObjectMapper().readValue(request.getInputStream(), 
                    AuthenticationRequest.class);
            return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
                    authRequest.getUsername(), authRequest.getPassword(), new ArrayList<>()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
            FilterChain chain, Authentication auth) throws IOException {
        Date expirationDate = DateUtil.getDateAddDays(new Date(), 1);
        String token = Jwts.builder().setIssuedAt(new Date()).setIssuer(WebSecurity.ISSUER)
                .setSubject(((ClientDetails)auth.getPrincipal()).getUsername())
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512, HardCodeUtil.JWT_KEY).compact();
        response.addHeader(WebSecurity.HEADER_AUTHORIZATION, WebSecurity.PREFIX_JWT + token);
        response.addHeader(WebSecurity.HEADER_JWT_EXPIRATION_DATE, String.valueOf(expirationDate.getTime()));
        ObjectMapper mapper = new ObjectMapper();
        ClientExtraParams extraParams = new ClientExtraParams((byte)1);
        String body = mapper.writeValueAsString(new ClientLoginResponse(((ClientDetails)auth.getPrincipal()).getClient(),
                extraParams));
        response.setContentType("application/json");
        response.getWriter().write(body);
        response.getWriter().flush();
        response.getWriter().close();
    }

}

第二个过滤器是在访问资源之前验证每个JWT:

public class JWTAuthorizationFilter extends BasicAuthenticationFilter {

    private static final Logger log = Logger.getLogger(JWTAuthorizationFilter.class.getName());

    public JWTAuthorizationFilter(AuthenticationManager authManager) {
        super(authManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        String header = req.getHeader(WebSecurity.HEADER_AUTHORIZATION);
        if (header == null || !header.startsWith(WebSecurity.PREFIX_JWT)) {
            chain.doFilter(req, res);
            return;
        }
        try {
            UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            chain.doFilter(req, res);
        }catch (SignatureException ex) {
            log.log(Level.SEVERE, "JWT SIGNING INVALID");
        }catch (MalformedJwtException ex) {
            log.log(Level.SEVERE, "JWT STRUCTURE INVALID");
        }catch (ExpiredJwtException ex) {
            log.log(Level.SEVERE, "JWT EXPIRED");
            GeneralResponse jwtInvalidResponse = new GeneralResponse(ErrorsEnum.JWT_EXPIRED);
            ObjectMapper mapper = new ObjectMapper();
            String body = mapper.writeValueAsString(jwtInvalidResponse);
            res.setContentType("application/json");
            res.getWriter().write(body);
            res.getWriter().flush();
            res.getWriter().close();
        }catch (UnsupportedJwtException ex) {
            log.log(Level.SEVERE, "JWT UNSUPPORTED");
        }catch (IllegalArgumentException ex) {
            log.log(Level.SEVERE, "ILLEGAL ARGUMENT JWT ENVIADO");
        }
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String token = request.getHeader(WebSecurity.HEADER_AUTHORIZATION);
        if (token != null) {
            String user = Jwts.parser()
                    .setSigningKey(HardCodeUtil.JWT_KEY)
                    .parseClaimsJws(token.replace(WebSecurity.PREFIX_JWT, ""))
                    .getBody()
                    .getSubject();
            if (user != null) {
                return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
            }
        }
        return null;
    }

}

在HttpSecurity的Spring配置中添加以下过滤器:

.and().addFilter(new JWTAuthenticationFilter(authenticationManager()))
                    .addFilter(new JWTAuthorizationFilter(authenticationManager()));

我使用这个库实现了这个:

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
 类似资料:
  • 我正在将应用程序的安全性迁移到Spring Security4.0。我的要求是身份验证应该是JAAS身份验证,自动化数据将从数据库中提取。所以我已经编写和自定义了身份验证提供程序。但我的问题是Spring没有将身份验证请求委托给我的自定义身份验证提供程序。 代码如下 web.xml条目 调用堆栈

  • 我有一个使用bouncycastle库的Java applet。当我在Eclipse上运行应用程序时,一切正常,但当我在带有标记的浏览器上使用applet时,当我添加安全BouncyCastleProvider时,它会引发异常。 我的stackTrace是: 我正在使用bcpkix-jdk15on-1.48.jar和bcprov-jdk15on-1.48.jar版本的BouncyCastle。为什

  • 在我的firebase应用程序中,用户可以使用 Google(Firebase的联邦提供商)或 Slack(作为自定义身份验证提供程序实现) 我想给用户链接两个帐户的机会。所以我要开的案子是: 用户使用Google登录 用户转到“设置”并单击“使用松弛连接” 然后应该链接用户帐户,以便他下次可以使用Slack或Google登录 我现在想知道,我是否可以通过某种方式从我的自定义松弛身份验证获得一个A

  • 我正在尝试使用WLST在线模式脚本添加一个新的自定义身份验证提供程序,但我得到一个类未找到异常,尽管我可以在WL控制台上看到我的提供程序。 情况如下: 我有一个JAR文件,它包含一个自定义WebLogic身份验证提供程序 JAR复制在user\u projects/domains/$DOMAIN\u NAME/lib/目录下 我可以在WL控制台上看到自定义身份验证提供程序,出现在列表中:

  • 我在Project中使用了Spring Boot和JWT以及Spring security。Spring Boot:2.3.5.发布JJWT Verison:0.11.2 我想要以下异常响应,这是我的自定义响应类:

  • 如何通过使用Spring Security和Java配置来定义自定义身份验证提供程序?我想在我自己的数据库上执行一个登录检查凭据。