我尝试构建一个简单的Spring Security应用程序,该应用程序注册用户,并具有1个端点,该端点应可供已验证用户使用。注册成功,但当我尝试使用已注册用户的基本身份验证访问/auth
EP时,我得到401未授权。
例如,我用以下公式计算POST /register
:
{
"name":"John",
"lastname":"Doe",
"email":"john@acme.com",
"password":"123"
}
然后调用GET /auth
用户名:john@acme.com
密码:123
我得到401
。
使用硬编码用户调用时:
用户名:admin
密码:123
我得到200
控制器
package com.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@Validated
public class AccountServiceController {
UserService userService;
@Autowired
public AccountServiceController(UserService userService) {
this.userService = userService;
}
@PostMapping("/register")
public ResponseEntity register(@RequestBody @Valid UserDTO user) {
return new ResponseEntity(userService.saveUser(user), HttpStatus.OK);
}
@GetMapping("/auth")
public String getAuth() {
return "Access granted";
}
}
用户实体
package com.AccountService;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import javax.persistence.*;
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
private String lastname;
private String email;
private String password;
public UserEntity(String name, String lastname, String email, String password) {
this.name = name;
this.lastname = lastname;
this.email = email;
this.password = password;
}
}
用户DTO
package com.AccountService;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {
private String name;
private String lastname;
@Pattern(regexp = "[a-zA-Z0-9.-]+@acme.com",message = "Wrong email")
private String email;
@NotEmpty
// @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
}
用户存储库
package com.AccountService;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
@Query("select u from UserEntity u where upper(u.email) = upper(?1)")
UserEntity findByEmailIgnoreCase(String email);
}
用户服务
package com.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class UserService {
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
UserRepository userRepository;
public Map<String, String> saveUser(UserDTO userDTO) {
userDTO.setPassword(passwordEncoder.encode(userDTO.getPassword()));
UserEntity newUser = new UserEntity(
userDTO.getName(),
userDTO.getLastname(),
userDTO.getEmail(),
userDTO.getPassword()
);
userRepository.save(newUser);
return Map.of(
"id", String.valueOf(newUser.getId()),
"name", newUser.getName(),
"lastName", newUser.getLastname(),
"email", newUser.getEmail());
}
}
用户详细信息实施
package com.AccountService;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Set;
public class UserDetailsImpl implements UserDetails {
private final String username;
private final String password;
public UserDetailsImpl(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Set.of();
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return false;
}
}
用户详细信息服务
package com.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
UserRepository userRepository;
@Autowired
public UserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntity user = userRepository.findByEmailIgnoreCase(username);
if (user == null) {
throw new UsernameNotFoundException("No user found with email" + username);
}
return new UserDetailsImpl(user.getEmail(),user.getPassword());
}
}
安全配置
package com.AccountService;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@EnableWebSecurity
@Configuration
@AllArgsConstructor
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
UserDetailsServiceImpl userDetailsService;
@Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.httpBasic()
.and()
.authorizeRequests()
.mvcMatchers("/auth").authenticated()
.mvcMatchers("/register").permitAll()
.and()
.csrf().disable()
.headers().frameOptions().disable();
}
@Override
public void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userDetailsService)
.passwordEncoder(getEncoder());
builder
.inMemoryAuthentication()
.withUser("admin").password("123").roles()
.and().passwordEncoder(NoOpPasswordEncoder.getInstance());
}
@Bean
public PasswordEncoder getEncoder() {
return new BCryptPasswordEncoder();
}
}
应用程序.属性
server.error.include-message=always
management.endpoints.web.exposure.include=*
management.endpoint.shutdown.enabled=true
spring.jackson.serialization.INDENT_OUTPUT=true
spring.datasource.url=jdbc:h2:file:../service_db
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true
spring.h2.console.settings.trace=false
spring.h2.console.settings.web-allow-others=false
spring.jpa.show-sql=true
构建.gradle
plugins {
id 'org.springframework.boot' version '2.7.3'
id 'io.spring.dependency-management' version '1.0.13.RELEASE'
id 'java'
}
group = 'com'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = "1.9"
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'junit:junit:4.13.1'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'com.unboundid:unboundid-ldapsdk'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation', version: '2.7.3'
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.9.0'
implementation 'org.springframework.boot:spring-boot-starter-security:2.7.3'
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
implementation group: 'org.hibernate', name: 'hibernate-validator', version: '6.1.0.Final'
}
tasks.named('test') {
useJUnitPlatform()
}
test {
useJUnitPlatform()
}
targetCompatibility = JavaVersion.VERSION_1_9
1条答案
按热度按时间mzsu5hc01#
[已解决]
UserDetailsImpl
中的布尔方法被设置为false
,导致用户被锁定。必须更改为return true;
。谢谢,@M.代努姆