Spring Security MultiHttpSecurity配置,这样我就可以执行两种类型的身份验证. JWT令牌和会话Cookie [英] Spring Security MultiHttpSecurity Configuration so that I can perform two types of authentication. JWT Token and Session Cookie

查看:151
本文介绍了Spring Security MultiHttpSecurity配置,这样我就可以执行两种类型的身份验证. JWT令牌和会话Cookie的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经为我的应用程序准备了Spring Security Cookie机制,现在仅针对API,我需要添加基于JWT令牌的身份验证机制.我正在使用带有两个嵌套类的Spring Security的MultiHttpSecurityConfiguration.

I already have Spring Security Cookie mechanism in place for my application, now only for the API's, I need to add JWT Token-based authentication mechanism. I'm using Spring Security's MultiHttpSecurityConfiguration with two nested class.

在一个应用程序中是否应该同时包含会话和JWT令牌机制是一个完全不同的问题,我需要实现两件事.

Whether both session and JWT token mechanism should be included together in one application or not is a different question altogether, I need to achieve two things.

  1. Spring Security基于cookie的基于会话的身份验证将像以前一样工作.
  2. 需要为API的
  3. 添加身份验证标头
  1. Spring Security's session-based authentication with cookie will work as it was before.
  2. Need to add an authentication header for API's

package com.leadwinner.sms.config;

import java.util.Collections;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import com.leadwinner.sms.CustomAuthenticationSuccessHandler;
import com.leadwinner.sms.CustomLogoutSuccessHandler;
import com.leadwinner.sms.config.jwt.JwtAuthenticationProvider;
import com.leadwinner.sms.config.jwt.JwtAuthenticationTokenFilter;
import com.leadwinner.sms.config.jwt.JwtSuccessHandler;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@ComponentScan(basePackages = "com.leadwinner.sms")
public class MultiHttpSecurityConfig {

    @Autowired
    @Qualifier("userServiceImpl")
    private UserDetailsService userServiceImpl;

    @Autowired
    private JwtAuthenticationProvider authenticationProvider;

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

    @Bean
    public PasswordEncoder passwordEncoder() {
        return  new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager() {
        return new ProviderManager(Collections.singletonList(authenticationProvider));
    }

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

         @Autowired
         private JwtAuthenticationTokenFilter jwtauthFilter;

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

    @Configuration
    @Order(2)
    public static class SecurityConfig extends WebSecurityConfigurerAdapter {
        private  final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);

        @Bean
        public CustomAuthenticationEntryPoint getBasicAuthEntryPoint() {
            return new CustomAuthenticationEntryPoint();
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {

            logger.info("http configure");
            http
            .antMatcher("/**").authorizeRequests()          
            .antMatchers("/login/authenticate").permitAll()
                    .antMatchers("/resources/js/**").permitAll()
                    .antMatchers("/resources/css/**").permitAll()
                    .antMatchers("/resources/images/**").permitAll()
                    .antMatchers("/web/initial/setup/**").permitAll()
                    .antMatchers("/dsinput/**").permitAll().antMatchers("/dsoutput/**").permitAll()                 

                    .and()
                .formLogin()
                    .loginPage("/login").usernameParameter("employeeId").passwordParameter("password")
                    .successForwardUrl("/dashboard")
                    .defaultSuccessUrl("/dashboard", true)
                    .successHandler(customAuthenticationSuccessHandler())
                    .failureForwardUrl("/logout")
                    .loginProcessingUrl("/j_spring_security_check")
                    .and().logout()
                    .logoutSuccessUrl("/logout").logoutUrl("/j_spring_security_logout")
                    .logoutSuccessHandler(customLogoutSuccessHandler())
                    .permitAll()
                    .invalidateHttpSession(true)
                    .deleteCookies("JSESSIONID")
                    .and().sessionManagement()
                    .sessionFixation().none()
                    .sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
                    .invalidSessionUrl("/logout")
                    .and().exceptionHandling().accessDeniedPage("/logout").and().csrf().disable();
            http.authorizeRequests().anyRequest().authenticated();


        }

        @Bean
        public AuthenticationSuccessHandler customAuthenticationSuccessHandler() {
            return new CustomAuthenticationSuccessHandler();
        }

        @Bean
        public LogoutSuccessHandler customLogoutSuccessHandler() {
            return new CustomLogoutSuccessHandler();
        }
    }
}

JwtAuthenticationTokenFilter.java

JwtAuthenticationTokenFilter.java

package com.leadwinner.sms.config.jwt;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

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)) {
                        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                                username, null, null);
                        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);
    }
}

JwtTokenUtil.java

JwtTokenUtil.java

package com.leadwinner.sms.config.jwt;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

import org.springframework.stereotype.Component;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

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

似乎JwtSecurityConfig过滤器现在未应用于我提到的路径. 任何帮助将不胜感激.

It seems that now the JwtSecurityConfig filter is not being applied for the path I have mentioned. Any help will be appreciated.

我已经阅读了这个问题.我也一样.

I have already read this question. I followed the same.

使用Spring Boot的Spring Security :将基本身份验证与JWT令牌身份验证混合在一起

添加了JwtAuthenticationTokenFilter,JwtTokenUtil

Added JwtAuthenticationTokenFilter, JwtTokenUtil

推荐答案

我知道了您的要求.

  1. 您需要在请求标头(对于每个请求)中公开应通过JWT令牌访问的API.
  2. Web应用程序也应通过基于表单的身份验证机制来保护,该机制应基于http会话进行工作.

您可以通过两个身份验证过滤器来实现这一目标.

You can achieve this by two authentication filters.

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

Filter - 1: for Rest API (JwtAuthTokenFilter) which should be stateless and identified by Authorization token sent in request each time.
Filter - 2: You need another filter (UsernamePasswordAuthenticationFilter) By default spring-security provides this if you configure it by http.formLogin(). Here each request is identified by the session(JSESSIONID cookie) associated. If request does not contain valid session then it will be redirected to authentication-entry-point (say: login-page).

api-url-pattern    = "/api/**" [strictly for @order(1)]
webApp-url-pattern = "/**" [ wild card "/**" always used for higer order otherwise next order configuration becomes dead configuration]

方法

  • 使用@EnableWebSecurity

    Approach

    • Define Main Configuration class with @EnableWebSecurity

      创建两个内部静态类,这些类应扩展WebSecurityConfigurerAdapter并使用@Configuration和@Order进行注释.在这里,其余api配置的顺序应为1,对于Web应用程序配置的顺序应大于1

      Create two inner static classes which should extend WebSecurityConfigurerAdapter and annotated with @Configuration and @Order. Here order for rest api configuration should be 1 and for web application configuration order should be more than 1

      请参阅 此链接中的我的答案以获取更多详细信息 ,该解释在深度与必要的代码.如果需要,请随时从github存储库中请求可下载的链接.

      Refer my answer in this link for more details which has explaination in depth with necessary code. Feel free to ask for downloadable link from github repository if required.

      限制
      这两个过滤器将并排(平行)工作.我的意思是,即使用户通过会话进行了身份验证,从Web应用程序来说,如果没有JWT令牌,他也无法访问API.

      Limitation
      Here both filters will work side by side(Parellally). I mean from web application even though if a user is authenticated by session, he can not access API's without a JWT token.

      编辑
      对于OP的要求,他不想定义任何角色,但允许经过身份验证的用户访问API.对于他的要求,修改了以下配置.

      EDIT
      For OP's requirement where he doesn't want to define any role but API access is allowed for authenticated user. For his requirement modified below configuration.

      http.csrf().disable()
      .antMatcher("/web/umgmt/**").authorizeRequests()
      .antMatcher("/web/umgmt/**").authenticated() // use this
      

      这篇关于Spring Security MultiHttpSecurity配置,这样我就可以执行两种类型的身份验证. JWT令牌和会话Cookie的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆