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

Spring Boot Security+JWT

白飞飙
2023-03-14
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private static final String SALT = "fd23451*(_)nof";

    private final JwtAuthenticationEntryPoint unauthorizedHandler;
    private final JwtTokenUtil jwtTokenUtil;
    private final UserSecurityService userSecurityService;

    @Value("${jwt.header}")
    private String tokenHeader;


    public ApiWebSecurityConfig(JwtAuthenticationEntryPoint unauthorizedHandler, JwtTokenUtil jwtTokenUtil,
            UserSecurityService userSecurityService) {
        this.unauthorizedHandler = unauthorizedHandler;
        this.jwtTokenUtil = jwtTokenUtil;
        this.userSecurityService = userSecurityService;
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userSecurityService)
                .passwordEncoder(passwordEncoder());
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(12, new SecureRandom(SALT.getBytes()));
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {

        httpSecurity
                // we don't need CSRF because our token is invulnerable
                .csrf().disable()

                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()

                // don't create session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                // Un-secure H2 Database
                .antMatchers("/h2-console/**/**").permitAll()
                .antMatchers("/api/v1/users").permitAll()
                .anyRequest().authenticated();

        // Custom JWT based security filter
        JwtAuthorizationTokenFilter authenticationTokenFilter = new JwtAuthorizationTokenFilter(userDetailsService(), jwtTokenUtil, tokenHeader);
        httpSecurity
                .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        // disable page caching
        httpSecurity
                .headers()
                .frameOptions()
                .sameOrigin()  // required to set for H2 else H2 Console will be blank.
                .cacheControl();
    }

    @Override
    public void configure(WebSecurity web) {

        // AuthenticationTokenFilter will ignore the below paths
        web
                .ignoring()
                .antMatchers(
                        HttpMethod.POST,
                        "/api/v1/users"
                );

    }

}
@Provider
@Slf4j
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {

    private UserDetailsService userDetailsService;
    private JwtTokenUtil jwtTokenUtil;
    private String tokenHeader;

    public JwtAuthorizationTokenFilter(UserDetailsService userDetailsService, JwtTokenUtil jwtTokenUtil, String tokenHeader) {
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;
        this.tokenHeader = tokenHeader;
    }


    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        return new AntPathMatcher().match("/api/v1/users", request.getServletPath());
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException,
            IOException {

        log.info("processing authentication for '{}'", request.getRequestURL());

        final String requestHeader = request.getHeader(this.tokenHeader);

        String username = null;
        String authToken = null;

        if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
            authToken = requestHeader.substring(7);
            try {
                username = jwtTokenUtil.getUsernameFromToken(authToken);
            } catch (IllegalArgumentException e) {
                logger.info("an error occured during getting username from token", e);
            } catch (ExpiredJwtException e) {
                logger.info("the token is expired and not valid anymore", e);
            }
        } else {
            logger.info("couldn't find bearer string, will ignore the header");
        }

        log.info("checking authentication for user '{}'", username);

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            logger.info("security context was null, so authorizating user");

            // It is not compelling necessary to load the use details from the database. You could also store the information
            // in the token and read it from it. It's up to you ;)
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);

            // For simple validation it is completely sufficient to just check the token integrity. You don't have to call
            // the database compellingly. Again it's up to you ;)
            if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                log.info("authorizated user '{}', setting security context", username);
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        chain.doFilter(request, response);
    }
}
@Component
@Slf4j
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {

    private static final long serialVersionUID = -8970718410437077606L;

    @Override
    public void commence(HttpServletRequest request,
            HttpServletResponse response,
            AuthenticationException authException) throws IOException {

        log.info("user tries to access a secured REST resource without supplying any credentials");

        // This is invoked when user tries to access a secured REST resource without supplying any credentials
        // We should just send a 401 Unauthorized response because there is no 'login page' to redirect to
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }
}

这是我启动应用程序时的控制台:

18:02:51.974 [restartedMain] DEBUG com.agrumh.Application - Running with Spring Boot v2.4.2, Spring v5.3.3
18:02:51.974 [restartedMain] INFO  com.agrumh.Application - No active profile set, falling back to default profiles: default
18:02:57.383 [restartedMain] INFO  o.s.s.web.DefaultSecurityFilterChain - Will secure Ant [pattern='/api/v1/users', POST] with []
18:02:57.414 [restartedMain] DEBUG o.s.s.w.a.e.ExpressionBasedFilterInvocationSecurityMetadataSource - Adding web access control expression [permitAll] for Ant [pattern='/h2-console/**/**']
18:02:57.415 [restartedMain] DEBUG o.s.s.w.a.e.ExpressionBasedFilterInvocationSecurityMetadataSource - Adding web access control expression [permitAll] for Ant [pattern='/api/v1/users']
18:02:57.416 [restartedMain] DEBUG o.s.s.w.a.e.ExpressionBasedFilterInvocationSecurityMetadataSource - Adding web access control expression [authenticated] for any request
18:02:57.422 [restartedMain] INFO  o.s.s.web.DefaultSecurityFilterChain - Will secure any request with [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@24c68fed, org.springframework.security.web.context.SecurityContextPersistenceFilter@1537eb0a, org.springframework.security.web.header.HeaderWriterFilter@95de45c, org.springframework.security.web.authentication.logout.LogoutFilter@733cf550, com.dispacks.config.JwtAuthorizationTokenFilter@538a96c8, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@8d585b2, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@784cf061, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@64915f19, org.springframework.security.web.session.SessionManagementFilter@21f180d0, org.springframework.security.web.access.ExceptionTranslationFilter@2b153a28, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@4942d157]
18:02:58.619 [restartedMain] INFO  com.dispacks.DispacksApplication - Started DispacksApplication in 6.974 seconds (JVM running for 7.697)
18:04:03.685 [http-nio-1133-exec-1] DEBUG o.s.security.web.FilterChainProxy - Securing POST /error
18:04:03.687 [http-nio-1133-exec-1] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - Set SecurityContextHolder to empty SecurityContext
18:04:03.689 [http-nio-1133-exec-1] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - Set SecurityContextHolder to anonymous SecurityContext
18:04:03.694 [http-nio-1133-exec-1] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Failed to authorize filter invocation [POST /error] with attributes [authenticated]
18:04:03.698 [http-nio-1133-exec-1] INFO  c.d.s.JwtAuthenticationEntryPoint - user tries to access a secured REST resource without supplying any credentials
18:04:03.699 [http-nio-1133-exec-1] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - Cleared SecurityContextHolder to complete request

但是当我用邮递员访问时,我有一个错误:

22:58:33.562 [http-nio-1133-exec-2] WARN  o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/plain' not supported]
22:58:33.579 [http-nio-1133-exec-2] INFO  c.d.s.JwtAuthenticationEntryPoint - user tries to access a secured REST resource without supplying any credentials

共有1个答案

华温书
2023-03-14

授权和身份验证不同允许POST/api/v1/users,因为访问资源POST不需要经过授权。

在您的代码中,

    @Override
    public void commence(HttpServletRequest request,
            HttpServletResponse response,
            AuthenticationException authException // AuthenticationException means authentication failed, not "without supplying any credentials".
    ) throws IOException {

// Break point here, or print authException.

        log.info("user tries to access a secured REST resource without supplying any credentials"); // Wrong message. You can say "Authentication failed.", or log.info(authException.getMessage()).

        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }

身份验证错误实际上发生在访问/error资源时。

18:04:03.694 [http-nio-1133-exec-1] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Failed to authorize filter invocation [POST /error] with attributes [authenticated]
    null
 类似资料:
  • 问题内容: 我正在使用制作REST API 。我正在使用(https://github.com/auth0/java- jwt )进行令牌生成工作。请检查以下代码。 UserJSONInfo -REST方法类 UserInfoService- 服务层 AuthKey-仅 包含 web.xml 我将令牌生成类维护为另一个Java项目,并将其作为库导入此处(我正在使用Netbeans)。下面是代码 现

  • 问题内容: 我正在尝试使用JWT实施spring AuthorizationServer。我能够生成JWT令牌并登录,直到将BCrypt添加到混合中为止。现在,当我尝试登录时,我从API获得“错误的凭据”。 OAuth2Configuration.java WebSecurityConfig.java SeedData.java 谢谢你的帮助。 问题答案: 我需要进行以下更改才能使其正常工作。如果

  • 问题内容: 我有一个JAX- RS服务,我希望所有用户都可以访问我的服务,但只有那些有权查看结果的用户才能访问我的服务。基于角色的安全性以及现有的REALMS和不良处理方法不符合我的要求。 例如: 用户针对一项REST服务进行身份验证,我向他发送了带有其ID的JWT令牌 用户请求其他资源,并在每个请求中发送带有ID的JWT 我检查了他的用户ID(来自JWT),如果业务逻辑返回了结果,我将其发回,否

  • 问题内容: 我一直在寻找一个示例,我可以理解如何使用GO语言验证JWT的签名。 由于我使用的是Okta,而且使用的是JWK,所以这可能特别棘手,因此不是特别简单。 当我收到JWT时,可以毫无问题地对其进行解码,只是停留在如何验证签名上。 下面,我提供了一个JWT和JWK详细信息。如果有人对此感到满意,并且可以通过一个示例完成签名验证,那就太好了。:) 您可以在这里获得所有这些信息: https :

  • 问题内容: 我正在使用ExpressJS,Mongodb(Mogoose)构建应用程序。应用程序包含访问用户之前必须对其进行身份验证的路由。 目前,我已经编写了一个快速的中间件来做同样的事情。在这里,借助JWT令牌,我正在进行mongodb查询以检查用户是否已通过身份验证。但是感觉这可能会在我的数据库上增加不必要的请求负载。 我应该针对此特定任务集成redis吗? 会改善API性能吗?还是应该继续

  • 问题内容: 我正在尝试在身份验证系统中实现JWT,但我有几个问题。要存储令牌,我可以使用Cookie,但也可以使用或。 哪个是最佳选择? 我读过,JWT保护站点免受CSRF的侵害。但是,假设我将JWT令牌保存在cookie存储中,我无法想象它将如何工作。 那么如何保护它免受CSRF的侵害? 更新1 我看到了一些用法示例,如下所示: 当我从浏览器向服务器发出请求时,该如何实现?我还看到有人在URL中

  • 问题内容: 背景 我正在使用Spring Boot(1.3.0.BUILD-SNAPSHOT)设置一个RESTful Web应用程序,该应用程序包括一个STOMP / SockJS WebSocket,我打算从iOS应用程序和Web浏览器中使用它。我想使用JSON Web令牌(JWT)来保护REST请求和WebSocket接口,但后者遇到了困难。 该应用程序受Spring Security保护:

  • 问题内容: 如何验证从Amazon Cognito收到的JWT并从中获取信息? 我已经在Cognito中设置了Google身份验证,并将重定向uri设置为打API Gateway,然后收到了我发布到此端点的代码: https://docs.aws.amazon.com/cognito/latest/developerguide/token- endpoint.html 要接收JWT令牌,采用RS2