英文:
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("/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();
}
}
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("/**").permitAll()
, access to all except/api/register
, now response is 401 ?antMatchers("/**).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<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);
}
}
UserRepository
public interface UserRepository extends CrudRepository<User, Long> {
Optional<User> 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<? 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;
}
}
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("user1")
.password("password")
.roles("USER");
}
答案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("/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);
}
答案2
得分: 0
这是一个我自己付出了很大努力的错误。
由于我的天真和忽视了我处理异常的位置,事实上,在某些if/else语句中我返回了一个带有自定义异常的HTTP状态。这让我误以为用户身份验证没有起作用,实际上是有效的。
今天的教训:使用断点和调试
英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论