当前位置: 首页 > 面试题库 >

混合身份验证-基于Spring MVC会话+基于JWT令牌

璩华辉
2023-03-14
问题内容

我有一种情况,我正在使用Spring MVC(jsp,控制器,服务,dao)和基于会话的身份验证。但是现在我很少将这些URL用作用于集成目的的RESTful
Web服务。

仅对于那些请求,我需要使用基于令牌(例如,JWT)的身份验证。

因此,是否有可能在同一项目中同时使用两种类型的身份验证。


问题答案:

是否有可能在同一项目中同时使用两种身份验证。

是的你可以。通过具有两个身份验证处理过滤器。

过滤器-1 :用于Rest
API(JwtAuthTokenFilter),该API应该是无状态的,并由每次请求中发送的Authorization令牌标识。
过滤器2
:您需要另一个过滤器(UsernamePasswordAuthenticationFilter)默认情况下,如果通过进行配置,spring-
security将提供此过滤器http.formLogin()。在这里,每个请求都由JSESSIONID关联的session(cookie)标识。如果请求中不包含有效的会话,则它将被重定向到身份验证入口点(例如:login-
page)。

推荐的网址格式

api-url-pattern    = "/api/**"
webApp-url-pattern = "/**"

怎么运行的

  • URL的/api/**将会通过JwtAuthTokenFilter它将读取令牌的地方传递,如果它具有有效的令牌,则设置身份验证对象,并继续进行链接。如果没有有效的请求,则链会断开,并且将以401(未授权)状态发送响应。

  • URL以外的其他/api/**将由UsernamePasswordAuthenticationFilter[将由.formLogin()配置配置的Spring Security中的默认值]处理。它将检查有效的会话,如果它不包含有效的会话,它将重定向到配置的logoutSuccessUrl。

注意: 您的Webapp无法使用现有会话访问API。您具有的选择是使用Jwt令牌从Web应用程序访问API。

如何配置

要实现两个不同的身份验证处理过滤器,您应该以不同的顺序配置多个http安全配置,
可以通过在安全配置类中声明静态类来配置多个http安全配置,如下所示。
(即使OP要求在概念上明智地将代码明智地呈现出来,也可能有助于您参考)

Spring安全配置

@Configuration
@EnableWebSecurity
@ComponentScan(basePackages = "com.gmail.nlpraveennl")
public class SpringSecurityConfig
{
    @Bean
    public PasswordEncoder passwordEncoder() 
    {
        return new BCryptPasswordEncoder();
    }

    @Configuration
    @Order(1)
    public static class RestApiSecurityConfig extends WebSecurityConfigurerAdapter
    {
        @Autowired
        private JwtAuthenticationTokenFilter jwtauthFilter;

        @Override
        protected void configure(HttpSecurity http) throws Exception
        {
            http
                .csrf().disable()
                .antMatcher("/api/**")
                .authorizeRequests()
                .antMatchers("/api/authenticate").permitAll()
                .antMatchers("/api/**").hasAnyRole("APIUSER")
            .and()
                .addFilterBefore(jwtauthFilter, UsernamePasswordAuthenticationFilter.class);

            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }
    }

    @Configuration
    @Order(2)
    public static class LoginFormSecurityConfig extends WebSecurityConfigurerAdapter
    {
        @Autowired
        private PasswordEncoder passwordEncoder;

        @Autowired
        public void configureInMemoryAuthentication(AuthenticationManagerBuilder auth) throws Exception
        {
            auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder.encode("admin@123#")).roles("ADMIN");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception
        {
            http
                .csrf().disable()
                .antMatcher("/**").authorizeRequests()
                .antMatchers("/resources/**").permitAll()
                .antMatchers("/**").hasRole("ADMIN")
            .and().formLogin();

            http.sessionManagement().maximumSessions(1).expiredUrl("/customlogin?expired=true");
        }
    }
}

JWT身份验证令牌过滤器

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException
    {
        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)
                {
                    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);
    }
}

Jwt令牌util类

@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));
    }
}

分派器Servlet配置

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.gmail.nlpraveennl") //Do not skip componentscan
public class ServletConfiguration implements WebMvcConfigurer
{
     @Bean
     public ViewResolver configureViewResolver() 
     {
         InternalResourceViewResolver viewResolve = new InternalResourceViewResolver();
         viewResolve.setPrefix("/WEB-INF/jsp/");
         viewResolve.setSuffix(".jsp");

         return viewResolve;
     }

    @Bean
    public ResourceBundleMessageSource messageSource()
    {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setUseCodeAsDefaultMessage(true);
        return messageSource;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry)
    {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }
}


 类似资料:
  • 问题内容: 我正在PHP Lumen中构建一个应用程序,该应用程序在登录时会返回令牌。我不知道如何继续进行。 我应该如何使用这些令牌维护会话? 具体来说,如果我使用reactjs或原始HTML / CSS / jQuery,如何将令牌存储在客户端,并在我为Web应用程序的安全部分提出的每个请求中发送令牌? 问题答案: 我通常要做的是将令牌保留在本地存储中,这样即使用户离开站点,我也可以保留令牌。

  • 我正在PHP Lumen中构建一个应用程序,它在登录时返回令牌。我不知道如何继续。 我应该如何使用这些令牌维护会话? 具体来说,如果我使用reactjs或vanilla HTML/CSS/jQuery,我如何在客户端存储令牌,并在我为web应用程序的安全部分发出的每个请求中发送它们?

  • 我读了一些关于“JWT vs Cookie”的帖子,但它们只会让我更加困惑…… > 我想澄清一下,当人们谈论“基于令牌的身份验证与cookie”时,这里的cookie仅指会话cookie?我的理解是,cookie就像一个介质,它可以用来实现基于令牌的身份验证(在客户端存储可以识别登录用户的东西)或者基于会话的身份验证(在客户端存储与服务器端会话信息匹配的常量) 为什么我们需要JSON web令牌?

  • 我试图让Spring Security的基本身份验证与JWT令牌身份验证同时工作,但没有成功。我已经为web控制台和JWT实现了基本身份验证,以保护许多APIendpoint。下面是我的配置: } 我注意到,我用Order(1)注释的配置是Spring Security选择的配置,而另一个完全被忽略。像上面的配置一样,如果我尝试访问/console/login,我会得到401错误。任何帮助都将不胜

  • 问题内容: 我有一个REST API,我正在使用Spring Security基本授权进行身份验证,客户端会为每个请求发送用户名和密码。现在,我想实现基于令牌的身份验证,当用户最初通过身份验证时,我将在响应标头中发送令牌。对于进一步的请求,客户端可以在令牌中包含该令牌,该令牌将用于对资源进行用户身份验证。我有两个身份验证提供程序tokenAuthenticationProvider和daoAuth

  • 在daoAuthenticationProvider中,我设置了自定义userDetailsService并通过从数据库中获取用户登录详细信息对其进行身份验证(只要使用授权传递用户名和密码就可以正常工作:basic bgllqxbpvxnlcjogn21wxidmqjrdturtr04pag==作为头) 但是当我使用X-AUTH-TOKEN(即constants.auth_header_name)

  • null 我的自定义rest筛选器: 上面的内容实际上会导致应用程序启动时出现一个错误:有人能告诉我如何最好地执行此操作吗?pre_auth筛选器是执行此操作的最好方法吗? 编辑 使用Spring-security实现解决方案 希望它能帮助其他人…

  • 所以我正忙着为twitter开发一个应用程序,我希望人们使用PIN进行身份验证。(https://dev.twitter.com/oauth/pin-based). 要向人们显示PIN码,我需要使用以下命令:https://dev.twitter.com/oauth/reference/get/oauth/authorize 那里的例子说:https://api.twitter.com/oauth