Spring Security MultiHttpSecurity配置,这样我就可以执行两种身份验证JWT令牌和会话Cookie

如何解决Spring Security MultiHttpSecurity配置,这样我就可以执行两种身份验证JWT令牌和会话Cookie

我有你的要求

  1. 您需要在请求标头(针对每个请求)中公开应通过JWT令牌访问的API。

  2. Web应用程序也应通过基于表单的身份验证机制来保护,该机制应基于http会话进行工作。

您可以通过两个身份验证过滤器来实现。

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

  • 创建两个内部静态类,它们应WebSecurityConfigurerAdapter使用@Configuration和@Order进行扩展和注释。在此,REST API配置的顺序应为1,Web应用程序配置的顺序应大于1

  • 请参阅 ,其中 解释了必要的代码。如果需要,请随时从github存储库中请求可下载的链接。

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

OP的要求,即他不想定义任何角色,但允许经过身份验证的用户进行API访问。根据他的要求修改了以下配置。

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

解决方法

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

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

  1. Spring Security的基于cookie的基于会话的身份验证将像以前一样工作。

  2. 需要为API添加身份验证标头

    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

    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

    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筛选器现在并未应用于我提到的路径。任何帮助将不胜感激。

我已经读过这个问题。我也一样。

带有Spring Boot的SpringSecurity:将基本身份验证与JWT令牌身份验证混合

编辑:添加了JwtAuthenticationTokenFilter,JwtTokenUtil

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-