我目前正在使用 Spring Boot JWT MySQL 實施令牌和角色庫身份驗證,遵循教程 [https://github.com/team-learn-programming-yourself/jwt-youtube][1] 使用 Spring Security for REST API 進行身份驗證彈簧靴
它產生一個錯誤
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| jwtRequestFilter (field private com.example.jwt.service.JwtService com.example.jwt.configuration.JwtRequestFilter.jwtService)
↑ ↓
| jwtService (field private org.springframework.security.authentication.AuthenticationManager com.example.jwt.service.JwtService.authenticationManager)
↑ ↓
| webSecurityConfiguration (field private com.example.jwt.configuration.JwtRequestFilter com.example.jwt.configuration.WebSecurityConfiguration.jwtRequestFilter)
└─────┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
產生錯誤的依賴項的代碼如下:
jwtRequestFilter
package com.example.jwt.configuration;
import com.example.jwt.service.JwtService;
import com.example.jwt.util.JwtUtil;
import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
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;
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private JwtService jwtService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
System.out.println("JWT token does not start with Bearer");
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = jwtService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
網路安全配置
package com.example.jwt.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.AuthenticationManager;
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.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Autowired
private UserDetailsService jwtService;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors();
httpSecurity.csrf().disable()
.authorizeRequests().antMatchers("/authenticate", "/registerNewUser").permitAll()
.antMatchers(HttpHeaders.ALLOW).permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
;
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(jwtService).passwordEncoder(passwordEncoder());
}
}
JwtService
package com.example.jwt.service;
import com.example.jwt.dao.UserDao;
import com.example.jwt.entity.JwtRequest;
import com.example.jwt.entity.JwtResponse;
import com.example.jwt.entity.User;
import com.example.jwt.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.Set;
@Service
public class JwtService implements UserDetailsService {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserDao userDao;
@Autowired
private AuthenticationManager authenticationManager;
public JwtResponse createJwtToken(JwtRequest jwtRequest) throws Exception {
String userName = jwtRequest.getUserName();
String userPassword = jwtRequest.getUserPassword();
authenticate(userName, userPassword);
UserDetails userDetails = loadUserByUsername(userName);
String newGeneratedToken = jwtUtil.generateToken(userDetails);
User user = userDao.findById(userName).get();
return new JwtResponse(user, newGeneratedToken);
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.findById(username).get();
if (user != null) {
return new org.springframework.security.core.userdetails.User(
user.getUserName(),
user.getUserPassword(),
getAuthority(user)
);
} else {
throw new UsernameNotFoundException("User not found with username: " username);
}
}
private Set getAuthority(User user) {
Set<SimpleGrantedAuthority> authorities = new HashSet<>();
user.getRole().forEach(role -> {
authorities.add(new SimpleGrantedAuthority("ROLE_" role.getRoleName()));
});
return authorities;
}
private void authenticate(String userName, String userPassword) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userName, userPassword));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
}
uj5u.com熱心網友回復:
Spring security 自 2018 年以來已經全面支持 JWT,所以我的建議如下。
洗掉所有自定義代碼,因為撰寫自定義安全性是不好的做法。使用 Spring Security 附帶的內置 JWT 功能。
首先定義您要使用 oauth2resource 過濾器,但您希望過濾器處理 jwts 而不是傳統的 oauth2 令牌。
@EnableWebSecurity
public class DirectlyConfiguredJwtDecoder extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
}
}
Nimbus添加一個自定義解碼器,它將使用Spring Security 附帶的內置庫對 jwt 進行解碼。使用構建器模式NimbusJWTDecoder易于配置
只需將其添加為 a bean,spring 會自動將其拾取并將其注入到內置的 jwt 過濾器中。
@Bean
public JwtDecoder jwtDecoder() {
// Use builder functions to configure how the JWT should be validated.
return NimbusJwtDecoder.withPublicKey(this.key).build();
}
如果您希望配置范圍并將它們映射為應用程式中的角色,您只需添加一個JwtAuthenticationConverter將獲取范圍并將它們映射到具有ROLE_前綴的角色。如果您不添加轉換器,您的作用域將具有SCOPE_前綴。
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
請閱讀 Spring Security 中的 JWT 是如何作業的,有一整章介紹了如何在 Spring Security 中簡單輕松地實作 JWT 的處理。
Spring 安全 JWT
uj5u.com熱心網友回復:
從中洗掉
WebSecurityConfiguration:@Autowired private JwtRequestFilter jwtRequestFilter;問題是要能夠創建
jwtRequestFilter,您首先需要一個JwtService,它是在 中創建的WebSecurityConfiguration。jwtRequestFilter無緣無故地被注入到這個配置中(它不用于創建另一個@Bean)。由于是@Component. 通過@Autowire以上述方式使用,您使 WebSecurityConfiguration 依賴于過濾器,從而創建了這種回圈依賴。根據@M.Deinum 的評論,您
JwtService依賴于AuthenticationManager,這反過來又需要JwtService在構建時,特別是在這里:@Autowired public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder.userDetailsService(jwtService).passwordEncoder(passwordEncoder()); }JwtService應該只執行UserDetailsService.loadUserByUsername- 它不應該對其他任何事情負責。createJwtToken(JwtRequest)并且authenticate(String, String)是這里的罪犯。但是,您可以將它們移動到另一個類(這可能是 3.),這取決于您要創建此類令牌的原因、地點和時間,身份驗證本身的責任應由 AuthenticationManager 負責,并使用令牌生成User在認證通過后,以后的無密碼授權可以簡單地依賴于一個有效的引數。根本沒有理由createJwtToken應該包含任何呼叫authenticate。此處介紹了一種改進的方法(無從屬關系):https ://www.toptal.com/spring/spring-security-tutorial
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/419250.html
標籤:
