Configuring Springboot security, permitted path(s) not authorised 401

huangapple go评论57阅读模式
英文:

Configuring Springboot security, permitted path(s) not authorised 401

问题

以下是您提供的代码的翻译部分:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsService userDetailsService;

    /* This sets up the security on specified paths according to role of client */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .httpBasic()
                .and().authorizeRequests()
                .antMatchers("/api/quizzes/**").hasRole("USER")
                // .antMatchers("/api/register/").permitAll() // have tried this, still 401
                .antMatchers("/**").permitAll() // does not permit `/api/register` but does `/` and `h2-console`
                .and().headers().frameOptions().disable();
    }

    /* This sets up the user roles by searching the database for a match, so they can access the 
    endpoints configured above */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);

    }

    @Bean
    public PasswordEncoder getPasswordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}
@Service
public class UserService implements UserDetailsService {

    @Autowired
    static UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String email) {

        Optional<User> user = userRepository.findByEmail(email);
        System.out.println("loadByUsername called");
        if (user.isPresent()) {
            System.out.println("loadUserByUsername called: user.isPresent() = true");
            return new MyUserDetails(user.get().getEmail(), user.get().getPassword());
        } else {
            throw new UsernameNotFoundException("User: " + email + " not found");
        }
    }

    public static void saveUserToDB(User user) {

        if (user.getPassword().length() < 5) {
            throw new UsernameNotFoundException("password too short.");
        }

        Pattern pattern = Pattern.compile("simon.aust@hotmail.com");
        Matcher matcher = pattern.matcher(user.getEmail());

        if (!matcher.matches()) {
            throw new UsernameNotFoundException("email not correct format");
        }

        userRepository.save(user);
    }

}
public interface UserRepository extends CrudRepository<User, Long> {

    Optional<User> findByEmail(String email);

}
public class MyUserDetails implements UserDetails {

    private final String username;
    private final String password;

    public MyUserDetails(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
    }

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

    @Override
    public String getUsername() {
        return 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;
    }
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
    
    auth.inMemoryAuthentication()
        .passwordEncoder(org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance())
        .withUser("user1")
        .password("password")
        .roles("USER");

}

希望这些翻译对您有帮助。

英文:

I have a Springboot REST API which has the endpoints

  • /api/quizzes GET
  • /api/quizzes/{id} POST
  • /api/register POST
  • / GET which is a welcome page managed by a Thymeleaf controller class

I have set up my security class as follows;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsService userDetailsService;

    /* This sets up the security on specified paths according to role of client */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .httpBasic()
                .and().authorizeRequests()
                .antMatchers(&quot;/api/quizzes/**&quot;).hasRole(&quot;USER&quot;)
//                .antMatchers(&quot;/api/register/&quot;).permitAll() // have tried this, still 401
                .antMatchers(&quot;/**&quot;).permitAll() // does not permit `/api/register` but does `/` and `h2- 
                                                // console
                .and().headers().frameOptions().disable();
    }

    /* This sets up the user roles by searching the database for a match, so they can access the 
    endpoints configured above */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);

    }

    @Bean
    public PasswordEncoder getPasswordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}

Now when I try to access /api/register in Postman the response is 401 unauthorised. I can access /. I understood that /** is a wildcard for this directory and any subdirectories, so it should match root and /api/register, and permitAll()?

Edit: Some more info

  • comment out all the code in SecurityConfig, I get access to /, /api/quizzes, /api/quizzes/{id} but not /api/register, in fact it returns 403 instead of 401 which is interesting.
  • antMatchers(&quot;/**&quot;).permitAll() , access to all except /api/register , now response is 401 ?
  • antMatchers(&quot;/**).authenticated() , everything returns 401 unauthorised

I wonder, all the /api/quizzes endpoints are under the QuizController, but the /api/register endpoint is under its own @RestController controller class. Have I missed an annotation? I can't see it, it's setup the same.

I know Spring isn't looking at my UserService as the sout message isn't printed. I did have this working at some point yesterday, it was fetching the User from the database table. I'm not sure what has changed.

Here's my UserService


@Service
public class UserService implements UserDetailsService {

    @Autowired
    static UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String email) {

        Optional&lt;User&gt; user = userRepository.findByEmail(email);
        System.out.println(&quot;loadByUsername called&quot;);
        if (user.isPresent()) {
            System.out.println(&quot;loadUserByUsername called: user.isPresent() = true&quot;);
            return new MyUserDetails(user.get().getEmail(), user.get().getPassword());
        } else {
            throw new UsernameNotFoundException(&quot;User: &quot; + email + &quot; not found&quot;);
        }
    }

    public static void saveUserToDB(User user) {

        if (user.getPassword().length() &lt; 5) {
            throw new UsernameNotFoundException(&quot;password too short.&quot;);
        }

        Pattern pattern = Pattern.compile(&quot;simon\\.aust@hotmail\\.com&quot;);
        Matcher matcher = pattern.matcher(user.getEmail());

        if (!matcher.matches()) {
            throw new UsernameNotFoundException(&quot;email not correct format&quot;);
        }

        userRepository.save(user);
    }

}

UserRepository

public interface UserRepository extends CrudRepository&lt;User, Long&gt; {

    Optional&lt;User&gt; findByEmail(String email);

}

MyUserDetails

public class MyUserDetails implements UserDetails {

    private final String username;
    private final String password;

    public MyUserDetails(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {
        return Arrays.asList(new SimpleGrantedAuthority(&quot;ROLE_USER&quot;));
    }

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

    @Override
    public String getUsername() {
        return 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;
    }
}

Edit 2;
With in-memory authentication I can authenticate with certain endpoints, but again not /api/register

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
    
        auth.inMemoryAuthentication()
           .passwordEncoder(org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance())
                .withUser(&quot;user1&quot;)
                .password(&quot;password&quot;)
                .roles(&quot;USER&quot;);

}
    

答案1

得分: 2

尝试这个:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .csrf().disable()
            .httpBasic()
            .and()
            .authorizeRequests()
            .antMatchers("/api/register/**").permitAll()
            .antMatchers("/api/quizzes/**").authenticated()
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

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

请注意,这是Java代码,只有注释是英文的。

英文:

Try this:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .csrf().disable()
            .httpBasic()
            .and()
            .authorizeRequests()
            .antMatchers(&quot;/api/register/**&quot;).permitAll()
            .antMatchers(&quot;/api/quizzes/**&quot;).authenticated()
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

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

答案2

得分: 0

这是一个我自己付出了很大努力的错误。
由于我的天真和忽视了我处理异常的位置,事实上,在某些if/else语句中我返回了一个带有自定义异常的HTTP状态。这让我误以为用户身份验证没有起作用,实际上是有效的。
今天的教训:使用断点和调试 Configuring Springboot security, permitted path(s) not authorised 401

英文:

This is a mistake which I caused myself a great deal of effort over.
Due to my naivety and overlooking where I was handling my exceptions, I was in fact returning a HTTP status with a custom exception on some if/else statement. That was fooling me into believing user authentication was not working when in fact it was.
Lesson of the day: Use breakpoints and debug Configuring Springboot security, permitted path(s) not authorised 401

huangapple
  • 本文由 发表于 2020年7月30日 18:23:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/63171163.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定