我有你的要求
您需要在请求标头(针对每个请求)中公开应通过JWT令牌访问的API。
Web应用程序也应通过基于表单的身份验证机制来保护,该机制应基于http会话进行工作。
您可以通过两个身份验证过滤器来实现。
:用于RestAPI(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 SecurityCookie机制,现在仅针对API,我需要添加基于JWT令牌的身份验证机制。我正在使用带有两个嵌套类的SpringSecurity的MultiHttpSecurityConfiguration。
会话和JWT令牌机制是否应该一起包含在一个应用程序中是一个完全不同的问题,我需要实现两件事。
Spring Security的基于cookie的基于会话的身份验证将像以前一样工作。
需要为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;@Autowiredprivate JwtAuthenticationProvider authenticationProvider;@Autowiredpublic void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userServiceImpl).passwordEncoder(passwordEncoder());}@Beanpublic PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder();}@Beanpublic 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 {@Autowiredprivate JwtTokenUtil jwtTokenUtil;@Overrideprotected 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