我正在使用spring引导和Spring Security 创建一个api。我已经创建了一些基本的身份验证机制。目前在请求授权方面面临一些未知问题。这是我的配置类:
// removed for brevity
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final CustomUserDetailsService customUserDetailsService;
private final JwtTokenFilter jwtTokenFilter;
private final CustomAuthenticationProvider customAuthenticationProvider;
public SecurityConfiguration(CustomUserDetailsService customUserDetailsService,
JwtTokenFilter jwtTokenFilter,
CustomAuthenticationProvider customAuthenticationProvider) {
this.customUserDetailsService = customUserDetailsService;
this.jwtTokenFilter = jwtTokenFilter;
this.customAuthenticationProvider = customAuthenticationProvider;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// todo: provide an authenticationProvider for authenticationManager
/* todo:
In most use cases authenticationProvider extract user info from database.
To accomplish that, we need to implement userDetailsService (functional interface).
Here username is an email.
* */
auth.userDetailsService(customUserDetailsService);
auth.authenticationProvider(customAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// Enable CORS and disable CSRF
http = http.cors().and().csrf().disable();
// Set session management to Stateless
http = http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and();
// Set unauthorized requests exception handler
http = http
.exceptionHandling()
.authenticationEntryPoint(
(request, response, ex) -> {
response.sendError(
HttpServletResponse.SC_UNAUTHORIZED,
ex.getMessage()
);
}
)
.and();
// Set permissions and endpoints
http.authorizeRequests()
.antMatchers("/api/v1/auth/**").permitAll()
.antMatchers("/api/v1/beats/**").hasRole("ADMIN")
.anyRequest().authenticated();
http.addFilterBefore(jwtTokenFilter,
UsernamePasswordAuthenticationFilter.class);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// Used by spring security if CORS is enabled.
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
@Override @Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
}
}
为了检查用户是否有权访问资源,我使用jwt负载中的信息。为此,我有一个过滤器类:
// removed for brevity
@Component
public class JwtTokenFilter extends OncePerRequestFilter {
private final static Logger logger = LoggerFactory.getLogger(JwtTokenFilter.class);
private final JwtTokenUtil jwtTokenUtil;
private final CustomUserDetailsService customUserDetailsService;
public JwtTokenFilter(JwtTokenUtil jwtTokenUtil,
CustomUserDetailsService customUserDetailsService) {
this.jwtTokenUtil = jwtTokenUtil;
this.customUserDetailsService = customUserDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
final String header = request.getHeader(HttpHeaders.AUTHORIZATION);
if (header == null || header.isEmpty() || !header.startsWith("Bearer ")) {
logger.error("Authorization header missing");
filterChain.doFilter(request, response);
return;
}
final String token = header.split(" ")[1].trim();
if (!jwtTokenUtil.validate(token)) {
filterChain.doFilter(request, response);
return;
}
UserDetails userDetails = customUserDetailsService.loadUserByUsername(token);
if (userDetails == null)
throw new ServletException("Couldn't extract user from JWT credentials");
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, userDetails.getPassword(), userDetails.getAuthorities());
authentication.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
为了表示userdetails,我实现了customuserdetails和customuserdetails服务类:
@Data
@NoArgsConstructor
public class CustomUserDetails implements UserDetails {
private Long userId;
private Long profileId;
private String email;
private String password;
private String fullName;
private String nickname;
private String avatar;
private String phoneNumber;
private ProfileState profileState;
private Collection<? extends GrantedAuthority> grantedAuthorities;
public static CustomUserDetails fromUserAndProfileToMyUserDetails(Profile profile) {
CustomUserDetails customUserDetails = new CustomUserDetails();
customUserDetails.setUserId(profile.getUser().getId());
customUserDetails.setEmail(profile.getUser().getEmail());
customUserDetails.setPassword(profile.getUser().getPassword());
customUserDetails.setProfileId(profile.getId());
customUserDetails.setFullName(profile.getFullName());
customUserDetails.setNickname(profile.getNickname());
customUserDetails.setAvatar(profile.getAvatar());
customUserDetails.setPhoneNumber(profile.getPhoneNumber());
customUserDetails.setProfileState(profile.getState());
return customUserDetails;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return grantedAuthorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return nickname;
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return false;
}
}
customuserdetailsservice.java:
@Component
public class CustomUserDetailsService implements UserDetailsService {
private Logger logger = LoggerFactory.getLogger(CustomUserDetailsService.class);
private final ProfileRepository profileRepository;
private final JwtTokenUtil jwtTokenUtil;
public CustomUserDetailsService(ProfileRepository profileRepository, JwtTokenUtil jwtTokenUtil) {
this.profileRepository = profileRepository;
this.jwtTokenUtil = jwtTokenUtil;
}
@Override
public UserDetails loadUserByUsername(String token) throws UsernameNotFoundException {
if (token == null || token.isEmpty()) throw new IllegalArgumentException("Token cannot be null or empty");
try {
final String nickname = jwtTokenUtil.getNickname(token);
Profile profile = profileRepository
.findByNickname(nickname)
.orElseThrow(() -> new UsernameNotFoundException(
String.format("User: %s not found", token)
));
logger.info(String.format("Extracted Profile: %s", profile));
CustomUserDetails customUserDetails = CustomUserDetails.fromUserAndProfileToMyUserDetails(profile);
List<GrantedAuthority> authorities = new ArrayList<>(Collections.emptyList());
authorities.add(new SimpleGrantedAuthority(profile.getType().getValue()));
customUserDetails.setGrantedAuthorities(authorities);
return customUserDetails;
} catch (Exception e) {
logger.error("Wasn't able to load user `{}`. Exception occurred `{}`", token, e.getMessage());
return null;
}
}
}
这是我不想访问的控制器:
@RestController
@RequestMapping("/api/beats")
public class BeatController {
private static final Logger logger = LogManager.getLogger(BeatController.class);
private final BeatService beatService;
public BeatController(BeatService beatService) {
this.beatService = beatService;
}
@GetMapping("{id}")
public Object getBeat(@PathVariable Long id) {
try {
return beatService.findById(id);
} catch (Exception e) {
logger.error("Can't find beat with id " + id);
return new ResponseEntity<>(new DefaultResponseDto("failed", e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@GetMapping
public Object getBeats(@RequestParam String filter, @RequestParam String page) {
try {
return beatService.findAll();
} catch (Exception e) {
logger.error("Can't find beats");
return new ResponseEntity<>(new DefaultResponseDto("failed", e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping
public Object createBeat(@RequestBody BeatDto beatDto) {
try {
beatDto.setId(null);
return beatService.save(beatDto);
} catch (Exception e) {
logger.error("Can't create new Beat");
return new ResponseEntity<>(new DefaultResponseDto("failed", e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PutMapping("{id}")
public Object updateBeat(@PathVariable Long id, @RequestBody BeatDto newBeat) {
try{
BeatDto oldBeat = beatService.findById(id);
if (oldBeat != null) {
newBeat.setId(id);
} else {
throw new Exception();
}
return beatService.save(newBeat);
} catch (Exception e) {
return new ResponseEntity<>(new DefaultResponseDto("failed", e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@DeleteMapping("{id}")
public Object deleteBeat(@PathVariable Long id) {
try {
return beatService.deleteById(id);
} catch (Exception e) {
return new ResponseEntity<>(new DefaultResponseDto("failed", e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
因此,我发出一个请求,为它提供正确的授权头和访问令牌。它从db中获取一个用户,并获取grantedauthority。最后的步骤是:
它在securitycontext中设置身份验证对象。
在过滤链中走得更远。
但它不会到达控制器,也不会抛出任何异常。只回复我403。可能是我忘了设置什么,或者问题可能是其他什么?请引导我。
1条答案
按热度按时间bogh5gae1#
所以,我终于找到了问题所在。在这里帮助我的主要建议是:
customuserdetails服务中返回的所有方法
false
返回true
. (m。deinum)打开spring framework安全日志时使用:
logging.level.org.springframework.security=TRACE
. 这有助于我追踪一个异常,即filterchain抛出的异常。多亏了马库斯·赫特·达·科雷吉奥。我改变了什么来解决问题?首先我更新了
@RequestMapping
控制器不匹配。堆栈跟踪告诉我,虽然它正确地从数据库获取用户角色,但它无法匹配我的角色和我在配置类中编写的角色。默认情况下,它会在我们提供的实际角色名称之前添加“role_u2;”前缀。我认为定义这个bean会改变这种行为:事实证明,它不影响前缀行为,因此它在我提供的“admin”角色名称之前添加了“role_2;”。验证请求时添加“角色”前缀修复了以下问题:
从…起
到
此外,我还清理了gradle的构建和重建项目。感谢所有帮助过我的人!