我有一个springboot/springsecurity应用程序,我想在其中使用jwtauth和由springsecurity提供的jdbcuserdetailsmanager。
以下是我目前掌握的情况:
示例Application.java
package org.example.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
jwttokenfilter.java(使用userdetailsservice)
package org.example.app.auth;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import static org.springframework.util.ObjectUtils.isEmpty;
@Component
public class JwtTokenFilter extends OncePerRequestFilter {
private final JwtTokenUtil jwtTokenUtil;
private final UserDetailsService userDetailsService;
public JwtTokenFilter(JwtTokenUtil jwtTokenUtil, JdbcUserDetailsManager jdbcUserDetailsManager) {
this.jwtTokenUtil = jwtTokenUtil;
this.userDetailsService = jdbcUserDetailsManager;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
// Get authorization header and validate
final String header = request.getHeader(HttpHeaders.AUTHORIZATION);
if (isEmpty(header) || !header.startsWith("Bearer ")) {
chain.doFilter(request, response);
return;
}
// Get jwt token and validate
final String token = header.split(" ")[1].trim();
if (!jwtTokenUtil.validate(token)) {
chain.doFilter(request, response);
return;
}
// Get user identity and set it on the spring security context
String username = jwtTokenUtil.getUsername(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken
authentication = new UsernamePasswordAuthenticationToken(
userDetails, null,
userDetails == null ?
List.of() : userDetails.getAuthorities()
);
authentication.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
}
jwttokenutil.java(对于本例并不重要)
package org.example.app.auth;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;
import static java.lang.String.format;
@Component
public class JwtTokenUtil {
Logger logger = LoggerFactory.getLogger(JwtTokenUtil.class);
private final String jwtSecret = "aYEFtKMCn0xCg5caH1nnFuHfdAB0lBOvdonxq80VqOGNnG6QcyagXWOLrUdqJnzexUXYceMhGNFNYsA" +
"6rblSibUEh0yRsJ3XO1um1iMdoekOPzj4zKlokcu9TxTbz5DHYVLkqX3q9JrLgbLZFXD8ynOHfRHRL5Ge64iFZBVm9X517fwZrNornOm" +
"K2L7hUz10SgZpxAz6";
private final String jwtIssuer = "example.org";
public String generateAccessToken(User user) {
byte[] keyBytes = Decoders.BASE64.decode(jwtSecret);
Key key = Keys.hmacShaKeyFor(keyBytes);
return Jwts.builder()
.setSubject(format("%s", user.getUsername()))
.setIssuer(jwtIssuer)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 60 * 1000)) // 1 hour
.signWith(key, SignatureAlgorithm.HS512)
.compact();
}
public String getUsername(String token) {
JwtParser jwtParser = Jwts.parserBuilder()
.setSigningKey(jwtSecret).build();
Claims claims = jwtParser
.parseClaimsJws(token)
.getBody();
return claims.getSubject().split(",")[1];
}
public boolean validate(String token) {
try {
JwtParser jwtParser = Jwts.parserBuilder()
.setSigningKey(jwtSecret).build();
jwtParser.parseClaimsJws(token);
return true;
} catch (SecurityException ex) {
logger.error("Invalid JWT signature - {}", ex.getMessage());
} catch (MalformedJwtException ex) {
logger.error("Invalid JWT token - {}", ex.getMessage());
} catch (ExpiredJwtException ex) {
logger.error("Expired JWT token - {}", ex.getMessage());
} catch (UnsupportedJwtException ex) {
logger.error("Unsupported JWT token - {}", ex.getMessage());
} catch (IllegalArgumentException ex) {
logger.error("JWT claims string is empty - {}", ex.getMessage());
}
return false;
}
}
securityconfiguration.java(使用jwttokenfilter)
package org.example.app.config;
import org.example.app.auth.JwtTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;
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.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.sql.DataSource;
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final JwtTokenFilter jwtTokenFilter;
private final DataSource dataSource;
public SecurityConfiguration(JwtTokenFilter jwtTokenFilter, DataSource dataSource) {
this.jwtTokenFilter = jwtTokenFilter;
this.dataSource = dataSource;
}
public void configureHttpSecurity(HttpSecurity http) {
http.addFilterBefore(
jwtTokenFilter,
UsernamePasswordAuthenticationFilter.class
);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Autowired
@Bean
public JdbcUserDetailsManager userDetailsManager(AuthenticationManager authenticationManager,
AuthenticationManagerBuilder authenticationManagerBuilder)
throws Exception {
JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcUserDetailsManagerConfigurer =
authenticationManagerBuilder.jdbcAuthentication().dataSource(dataSource);
JdbcUserDetailsManager jdbcUserDetailsManager = jdbcUserDetailsManagerConfigurer.getUserDetailsService();
jdbcUserDetailsManager.setAuthenticationManager(authenticationManager);
return jdbcUserDetailsManager;
}
}
当应用程序尝试启动时,我看到以下内容:
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| jwtTokenFilter defined in file [/example-app/build/classes/java/main/org/example/app/auth/JwtTokenFilter.class]
↑ ↓
| securityConfiguration defined in file [/example-app/build/classes/java/main/org/example/app/config/SecurityConfiguration.class]
└─────┘
我想知道如何解决这个循环依赖,同时记住:
jwttokenfilter需要一个userdetailsmanager,因为我想在数据库中查找该用户,看看该用户是否仍然存在
securityconfiguration需要一个jwttokenfilter,它可以在httpsecurity上设置
免责声明:jwt相关代码的灵感来自https://www.toptal.com/spring/spring-security-tutorial
n、 b:我不确定在数据库中验证用户权限是否有意义,因为jwt的一个卖点是它是无状态的。。。我可以把当局放在jwt代币里。
2条答案
按热度按时间chhkpiq41#
看看@lazy annotation,我也遇到了同样的问题,而且效果很好。
尝试以下操作:
8cdiaqws2#
如果你使用
@Autowired
而不是构造函数注入。例子: