这一篇将进行 Security 从数据库读取用户信息及权限
数据库采用 Mysql, ROM 采用 JPA
首先进行 JPA 的配置, 在 application.properties 中添加(JPA 的使用将在后续文章中做记录)
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root
# jpa
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
一. 创建 SysUser 和 SysRole 及对应的数据库操作类(JPA)
这两个实体类使用的是 JPA 自动建表, dao 层继承 JpaRepository
SysUser类
Security 中的权限认证需要实现 UserDetails 接口 , 并复写其方法. getAuthorities() 方法返回的是权限点
package com.izuul.springsecurity.entity;
import lombok.Data;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
@Data
@Entity(name = "SYS_USER")
public class SysUser implements UserDetails, Serializable {
private static final long serialVersionUID = -2476551158093701699L;
@Id
@GenericGenerator(name = "system-uuid", strategy = "uuid2")
@GeneratedValue(generator = "system-uuid")
@Column(name = "ID")
private String id;
@Column(name = "USERNAME")
private String username;
@Column(name = "PASSWORD")
private String password;
@ManyToMany
@JoinTable(name = "USER_ROLE", joinColumns = @JoinColumn(name = "USER_ID", referencedColumnName = "ID"),
inverseJoinColumns = @JoinColumn(name = "ROLE_ID", referencedColumnName = "ID"))
private List<SysRole> sysRoles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return sysRoles;
}
@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;
}
}
SysRole 类
实现 GrantedAuthority 接口, 复写 getAuthority()方法
package com.izuul.springsecurity.entity;
import lombok.Data;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.security.core.GrantedAuthority;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.io.Serializable;
@Data
@Entity(name = "SYS_ROLE")
public class SysRole implements GrantedAuthority, Serializable {
private static final long serialVersionUID = -1834111111498772935L;
@Id
@GenericGenerator(name = "system-uuid", strategy = "uuid2")
@GeneratedValue(generator = "system-uuid")
@Column(name = "ID")
private String id;
@Column(name = "NAME")
private String name;
@Override
public String getAuthority() {
return name;
}
}
SysUserRepository
继承 JpaRepository, 写一个 findByUsername() 方法
package com.izuul.springsecurity.repository;
import com.izuul.springsecurity.entity.SysUser;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SysUserRepository extends JpaRepository<SysUser, String> {
SysUser findByUsername(String username);
}
SysRoleRepository
package com.izuul.springsecurity.repository;
import com.izuul.springsecurity.entity.SysRole;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SysRoleRepository extends JpaRepository<SysRole, String> {
}
二. 创建 UserDetailsService 实现类
Security 从数据库读取用户认证信息需要对 SecurityConfig 配置类进行更改
SecurityConfig 中需要引用 UserDetailsService 的实现类, 我先创建这个实现类
-
实现 UserDetailsService 接口 复写 loadUserByUsername(String username) 方法
-
创建初始化用户的 init() 方法
-
使用 PasswordEncoder 对密码进行加密
package com.izuul.springsecurity.service;
import com.izuul.springsecurity.entity.SysRole;
import com.izuul.springsecurity.entity.SysUser;
import com.izuul.springsecurity.repository.SysRoleRepository;
import com.izuul.springsecurity.repository.SysUserRepository;
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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
@Service
public class SysUserService implements UserDetailsService {
@Autowired
private SysUserRepository sysUserRepository;
@Autowired
private SysRoleRepository sysRoleRepository;
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 初始化2个用户
*/
@PostConstruct
private void init() {
if (sysUserRepository.findByUsername("admin") == null) {
SysRole roleAdmin = new SysRole();
roleAdmin.setName("ROLE_ADMIN");
SysUser sysAdmin = new SysUser();
sysAdmin.setUsername("admin");
sysAdmin.setPassword(passwordEncoder.encode("123"));
List<SysRole> sysRolesAdmin = new ArrayList<>();
sysRolesAdmin.add(roleAdmin);
sysAdmin.setSysRoles(sysRolesAdmin);
sysRoleRepository.save(roleAdmin);
sysUserRepository.save(sysAdmin);
}
if (sysUserRepository.findByUsername("user") == null) {
SysRole roleUser = new SysRole();
roleUser.setName("ROLE_USER");
SysUser sysUser = new SysUser();
sysUser.setUsername("user");
sysUser.setPassword(passwordEncoder.encode("123"));
List<SysRole> sysRolesUser = new ArrayList<>();
sysRolesUser.add(roleUser);
sysUser.setSysRoles(sysRolesUser);
sysRoleRepository.save(roleUser);
sysUserRepository.save(sysUser);
}
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return sysUserRepository.findByUsername(username);
}
}
二. 复写 configure(AuthenticationManagerBuilder auth) 方法
将上篇中从内存都认证信息的配置注销, 并复写 configure(AuthenticationManagerBuilder auth) 方法
@Autowired
private SysUserService sysUserService;
// @Autowired
// public void config(AuthenticationManagerBuilder auth) throws Exception {
// auth.inMemoryAuthentication()
// .passwordEncoder(passwordEncoder())
// .withUser("izuul")
// .password(passwordEncoder().encode("izuul"))
// .roles("USER");
// }
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(sysUserService);
}
配置好config 并连接数据库后, 启动项目
启动后会自动建表并插入初始化数据, 创建了两个用户 admin 和 user,
admin 用户的权限点是 ADMIN, user 用户的是 USER, 上篇中configure 做的资源配置如下
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 根路径 "/" 允许全部访问请求
.antMatchers("/").permitAll()
// 路径 "/user/**" 只允许
.antMatchers("/users/**").hasRole("USER")
.and()
.formLogin().loginPage("/login").failureUrl("/login-error")
.and()
.logout().logoutSuccessUrl("/logout-");
}
所以当我们使用 user 用户登录的时候可以正常访问
而 admin 用户登录的时候会抛出异常说明权限不够, 在实际项目中需要进行异常处理, 本文就不做相关展示了