Autenticación y autorización de Spring Security (2)

        Adaptar Spring Security al sistema, en lugar de adaptar el sistema a Spring Security , es el consenso de los desarrolladores y usuarios del marco Spring Security .

        A continuación, usaremos un modelo de base de datos personalizado para acceder a Spring Security , la base de datos sigue siendo MySQL y el marco de la capa de persistencia es MyBatis (los lectores que tienden a usar JPA también pueden elegir sus propios modelos, y su práctica en la parte de Spring Security es lo mismo). Nos centraremos en el contenido relacionado con Spring Security , por lo que se espera que los lectores lean la información relevante por sí mismos, o pueden optar por omitirla temporalmente.

1. Autenticación y autorización de modelos de bases de datos personalizados:

1. Implementar detalles de usuario:

        En el artículo anterior, usamos dos clases de implementación de UserDetailsService , InMemoryUserDetailsManager y JdbcUserDetailsManager . La forma de hacer efecto también es muy simple. Simplemente agregue el contenedor IoC de Spring , y Spring Security lo descubrirá y utilizará automáticamente . La estructura de la base de datos personalizada en realidad solo necesita implementar un UserDetailsService personalizado .

        UserDetailsService solo define un método loadUserByUsername para obtener un objeto UserDetails . El objeto UserDetails contiene una serie de información que se utilizará durante la autenticación, incluido el nombre de usuario, la contraseña, los permisos y otra información. Spring Security determinará si la autenticación es exitosa en función de esta información. El contenido del código fuente de UserDetails es el siguiente:

public interface UserDetails extends Serializable {

    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword();

    String getUsername();

    boolean isAccountNonExpired();

    boolean isAccountNonLocked();

    boolean isCredentialsNonExpired();

    boolean isEnabled();

}

        En otras palabras, no importa cómo cambie la estructura de la base de datos, siempre que se pueda construir un UserDetails , el proceso se realizará a continuación.

1.1 Preparación de la base de datos:

        Diseñe una estructura de base de datos personalizada. La declaración de creación de la tabla es la siguiente:

create table users(
	id bigint(20) not null auto_increment primary key,
	username varchar(50) not null,
	password varchar(60),
	enable tinyint(4) not null default '1' comment '用户是否可用',
	roles text character set utf8 comment '用户角色,多个用户角色用逗号隔开',
	KEY ‘username‘ (username)
);

        Ponemos la información y los roles de los usuarios en la misma tabla, que ya no es la forma separada predeterminada de Spring Security . El campo de roles se establece en tipo de texto y los roles múltiples están separados por comas. Se recomienda construir un índice en el campo de nombre de usuario para mejorar la velocidad de búsqueda.La estructura de la tabla es la siguiente:

        A continuación, inserte dos registros para facilitar nuestro trabajo de prueba posterior:

insert into users(username,password,roles) values ("admin","123","ROLE_ADMIN,ROLE_USER");
insert into users(username,password,roles) values ("user","123","ROLE_USER");

1.2 Implementación de codificación:

        Cuando la estructura de la base de datos y los datos están listos, se puede escribir la entidad de usuario correspondiente.

public class User {

	private Long id;
	
	private String username;
	
	private String password;
	
	private String roles;
	
	private boolean enable;

    // setter getter
}

        Deje que la entidad User herede UserDetails , el código es el siguiente:

public class User implements UserDetails{

	private Long id;
	private String username;
	private String password;
	private String roles;
	private boolean enable;

	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getRoles() {
		return roles;
	}
	public void setRoles(String roles) {
		this.roles = roles;
	}
	public boolean isEnable() {
		return enable;
	}
	public void setEnable(boolean enable) {
		this.enable = enable;
	}
	private List<GrantedAuthority> authorities;
	public void setAuthorities(List<GrantedAuthority> authorities) {
		this.authorities = authorities;
	}
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		// TODO Auto-generated method stub
		return this.authorities;
	}
	@Override
	public boolean isAccountNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}
	@Override
	public boolean isAccountNonLocked() {
		// TODO Auto-generated method stub
		return true;
	}
	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}
	@Override
	public boolean isEnabled() {
		// TODO Auto-generated method stub
		return this.enable;
	}
}

        Aquí debe implementar varios métodos definidos por  UserDetails .Entre ellos, los métodos isAccountNonExpired() , isAccountNonLocked()isCredentialsNonExpired() no se utilizan por el momento y devuelven true uniformemente , de lo contrario,  Spring Security considerará que la cuenta es anormal. isEnabled corresponde al campo habilitado , simplemente sustitúyalo. El método getAuthorities() en sí mismo corresponde al campo de funciones , pero debido a que la estructura es inconsistente, cree una nueva aquí y rellénela más tarde.

2. Implementar UserDetailsService:

2.1 Preparación de la capa de persistencia de datos:

Cuando UserDetails         esté listo , use el marco de la capa de persistencia de la base de datos para leer los datos y completar el objeto. Primero presenta MyBatis .

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.1</version>
</dependency>

        La configuración relacionada con la base de datos se escribió anteriormente en el archivo de configuración y se puede usar aquí.

spring.datasource.url = jdbc:mysql://localhost:3306/springDemo?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username = root
spring.datasource.password = Rfid123456

A continuación, use @MapperScan         en la clase de entrada para especificar el directorio del archivo de mapeo que MyBatis escaneará .

@SpringBootApplication
@RestController
@MapperScan("com.mapper")
public class SpringDemoApplication {

	@GetMapping("/")
	public String hello() {
		return "hello spring security";
	}
	
	public static void main(String[] args) {
		SpringApplication.run(SpringDemoApplication.class,args);
	}
}

        Por supuesto, también necesitamos crear el directorio en com.mapper y escribir la interfaz de mapeo correspondiente:

@Component
public interface UserMapper {

	@Select("SELECT * FROM  users where username=#{username}")
	User findByUserName(@Param("username") String username);
}

        El contenido relacionado con MyBatis no se describirá en detalle. Los lectores que no han estado en contacto con él y están interesados ​​pueden aprender el conocimiento relevante por sí mismos. Aquí solo se muestra un método para encontrar usuarios por nombre de usuario.

2.2 Implementación de codificación:

        Cuando la capa de persistencia de datos está lista, comenzamos a escribir UserDetailsService .

@Service
public class MyUserDetailsService implements UserDetailsService{

	@Autowired
	private UserMapper usermapper;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// 从数据库尝试读取该用户
		User user = usermapper.findByUserName(username);
		// 用户不存在,抛出异常
		if(user == null) {
			throw new UsernameNotFoundException("用户名不存在");
		}
		// 将数据库形式的 roles 解析为 UserDetails 的权限集
		// AuthorityUtils.commaSeparatedStringToAuthorityList() 是Spring Security 提供的
		// 该方法用于将逗号隔开的权限集字符串切割成可用权限对象列表
		// 当然也可以自己实现,如用分号来隔开等,参考下面的generateAuthorities()方法
		user.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles()));
		return user;
	}

	// 自行实现权限的转移
	private List<GrantedAuthority> generateAuthorities(String roles){
		List<GrantedAuthority> list = new ArrayList<>();
		String [] roleArray = roles.split(";");
		if(roles != null && !"".equals(roles)) {
			for(String role:roleArray) {
				list.add(new SimpleGrantedAuthority(role));
			}
		}
		return list;
	}
}

        Entre ellos, SimpleGrantedAuthority es una clase de implementación de GrantedAuthority . Los permisos de Spring Security casi se generan con SimpleGrantedAuthority , solo tenga en cuenta que cada rol corresponde a una GrantedAuthority . Además, debe agregar la anotación @Service a su clase de implementación UserDetailsService para que Spring Security pueda descubrirla automáticamente .

        Hasta ahora, hemos implementado la autenticación de estructura de base de datos personalizada de Spring Security . Algunos lectores pueden tener dudas, ¿por qué los roles en la base de datos siempre tienen que tener el prefijo "ROLE" , pero no hay un prefijo "ROLE" en la configuración ?

protected void configure(HttpSecurity http) throws Exception{
		http.authorizeRequests().
			antMatchers("/admin/api/**").hasRole("ADMIN").
			antMatchers("/user/api/**").hasRole("USER").
			antMatchers("/app/api/**").permitAll().
			anyRequest().authenticated().
		and().formLogin();
	}

        Puede encontrar la respuesta mirando el código fuente.

private static String hasRole(String role) {
		Assert.notNull(role, "role cannot be null");
		if (role.startsWith("ROLE_")) {
			throw new IllegalArgumentException(
					"role should not start with 'ROLE_' since it is automatically inserted. Got '"
							+ role + "'");
		}
		return "hasRole('ROLE_" + role + "')";
	}

        Si no desea hacer coincidir este prefijo, simplemente llame al método hasAuthority() en su lugar.

2.3 Prueba de efecto:

El código de WebSecurityConfig         en este momento  es el siguiente:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

    protected void configure(HttpSecurity http) throws Exception{
		http.authorizeRequests().
			antMatchers("/admin/api/**").hasRole("ADMIN").
			antMatchers("/user/api/**").hasRole("USER").
			antMatchers("/app/api/**").permitAll().
			anyRequest().authenticated().
		and().formLogin();
	}
}

        Inicie el programa, use la cuenta de usuario para iniciar sesión en  localhost:8080/user/api/hello y podrá iniciar sesión normalmente, como se muestra a continuación:

         Use la  cuenta de administrador para iniciar sesión en  localhost:8080/admin/api/hello y podrá iniciar sesión normalmente, como se muestra a continuación:

         Use la cuenta de usuario para iniciar sesión en localhost:8080/admin/api/hello , pero no puede iniciar sesión normalmente, como se muestra a continuación:

Supongo que te gusta

Origin blog.csdn.net/xhf852963/article/details/123877165
Recomendado
Clasificación