spring引导请求无法到达控制器

xesrikrc  于 2021-08-20  发布在  Java
关注(0)|答案(1)|浏览(348)

我正在使用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。可能是我忘了设置什么,或者问题可能是其他什么?请引导我。

bogh5gae

bogh5gae1#

所以,我终于找到了问题所在。在这里帮助我的主要建议是:
customuserdetails服务中返回的所有方法 false 返回 true . (m。deinum)
打开spring framework安全日志时使用: logging.level.org.springframework.security=TRACE . 这有助于我追踪一个异常,即filterchain抛出的异常。多亏了马库斯·赫特·达·科雷吉奥。
我改变了什么来解决问题?首先我更新了 @RequestMapping 控制器不匹配。堆栈跟踪告诉我,虽然它正确地从数据库获取用户角色,但它无法匹配我的角色和我在配置类中编写的角色。默认情况下,它会在我们提供的实际角色名称之前添加“role_u2;”前缀。我认为定义这个bean会改变这种行为:

GrantedAuthorityDefaults grantedAuthorityDefaults() {
        return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
    }

事实证明,它不影响前缀行为,因此它在我提供的“admin”角色名称之前添加了“role_2;”。验证请求时添加“角色”前缀修复了以下问题:
从…起

authorities.add(new SimpleGrantedAuthority(profile.getType().getValue()));

authorities.add(new SimpleGrantedAuthority("ROLE_" + profile.getType().getValue()));

此外,我还清理了gradle的构建和重建项目。感谢所有帮助过我的人!

相关问题