Spring Security 401未经授权。看起来与filter或allowAll()有关的问题

时间:2019-12-04 09:52:24

标签: java spring-boot spring-security jwt-auth

//我的第一个答案中的问题解决方案。

我简化了一个简单的Spring Security项目,并且似乎做得正确,因为我之前做过,而且几乎可以使用相同的代码来完成所有工作,但是现在我无法通过“ / auth / login”进行请求。

有趣的是,它在配置类http.antMatchers('/ auth / **“)。permitAll中,但是我只能通过/ auth / reg路径访问/ auth / login-返回401。

也许有人熟悉这个问题,很乐意帮助我解决这个问题。

我的安全配置类:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
        prePostEnabled = true
)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private UserDetailsServiceImpl userDetailsService;
    private JwtEntryPoint entryPoint;

    @Autowired
    public WebSecurityConfig(UserDetailsServiceImpl userDetailsService,
                             JwtEntryPoint entryPoint) {
        this.userDetailsService = userDetailsService;
        this.entryPoint = entryPoint;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
                .authorizeRequests()
                .antMatchers("/auth/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .exceptionHandling().authenticationEntryPoint(entryPoint)
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public JwtTokenFilter jwtTokenFilter() {
        return new JwtTokenFilter();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

RestController:

@RestController
@RequestMapping("/auth")
public class AuthController {

    private AuthenticationManager authManager;
    private UserRepository userRepository;
    private RoleRepository roleRepository;
    private PasswordEncoder encoder;
    private JwtTokenProvider tokenProvider;

    @Autowired
    public AuthController(AuthenticationManager authManager,
                          UserRepository userRepository,
                          RoleRepository roleRepository,
                          PasswordEncoder encoder,
                          JwtTokenProvider provider) {
        this.authManager = authManager;
        this.userRepository = userRepository;
        this.roleRepository = roleRepository;
        this.encoder = encoder;
        this.tokenProvider = provider;
    }

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginForm loginForm) {
        Authentication authentication = authManager.authenticate(
                new UsernamePasswordAuthenticationToken(loginForm.getUsername(), loginForm.getPassword()));
        SecurityContextHolder.getContext().setAuthentication(authentication);

        String token = tokenProvider.generateJwtToken(authentication);
        UserDetails userPrincipal = (UserDetails) authentication.getPrincipal();
        return ResponseEntity.ok(new JwtResponse(token, userPrincipal.getUsername(), userPrincipal.getAuthorities()));
    }

    @PostMapping("/reg")
    public ResponseEntity<?> register(@ModelAttribute RegForm regForm) {
        if (userRepository.existsUserByUsername(regForm.getUsername()))
            return ResponseEntity.badRequest().body("This username is already taken! Choose another one!");
        User user = new User(regForm.getUsername(),
                encoder.encode(regForm.getPassword()),
                UploadFileUtil.getStoragePath(regForm.getFile().getOriginalFilename()));
        Set<Role> defaultRoles = new HashSet<>();
        defaultRoles.add(roleRepository.findRoleByUserRole(Roles.USER));
        user.setUserRoles(defaultRoles);
        userRepository.save(user);
        return ResponseEntity.ok().body("User registered successfully!");
    }
}

感谢任何帮助。

1 个答案:

答案 0 :(得分:0)

原因不在安全配置中,而是在UserPrincipal类中实现UserDetails。当您实现包括getter在内的所有方法时,默认情况下它们将返回false和null。因此,当我尝试从委托人那里获取字段时,它们将null返回给Authentication,因此密码不相同,并且我得到了未经授权的响应401。

因此,不要忘记以正确的方式覆盖方法,并且不会浪费时间来处理奇怪的错误。

感谢所有提供帮助的人,

public class UserPrincipal implements UserDetails {

    private long id;
    private String username;
    @JsonIgnore
    private String password;
    private Collection<? extends GrantedAuthority> authorities;

    public static UserPrincipal build(User user) {
        List<GrantedAuthority> authorities = user.getUserRoles().stream().map(role ->
            new SimpleGrantedAuthority(role.getUserRole().name())
        ).collect(Collectors.toList());
        return new UserPrincipal(
                    user.getId(),
                    user.getUsername(),
                    user.getPassword(),
                    authorities
        );    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public String getUsername() {
        return this.username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }