设置登录系统的账户、密码
方法一、配置文件
在application.properties配置
spring.security.user.name= "tom"
spring.security.user.password= "123456"
方法二、配置类
新建一个配置类SecurityConfig
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public UserDetailsService userDetailsService(){
UserDetails admin = User.builder().username("tom").password("{noop}123456").roles("ADMIN").build();
UserDetails user = User.builder().username("LiSi").password("{noop}123456").roles("USER").build();
return new InMemoryUserDetailsManager(admin,user);
}
}
//roles则是权限设置
其中密码我们不会直接显示出来,我们需要进行加密,俗称加盐,在密码前面加上{noop}表示无操作,这是为了方便演示。我们在改写一下这个demo让其加密存储密码,帮助我们加密PasswordEncoder接口,我们将其实现
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService(){
UserDetails admin = User.builder().username("tom").password(passwordEncoder().encode("123456")).roles("ADMIN").build();
UserDetails user = User.builder().username("LiSi").password(passwordEncoder().encode("123456")).roles("USER").build();
return new InMemoryUserDetailsManager(admin,user);
}
}
这样我们的密码就是一个加密的状态,可以输出出来看一下。
方法三、数据库
前面两种方法只是简单的登录账户、密码的设置。在实际开发中用户的账户和密码,我们都是从数据库中获取。
1.首先我们要有一个数据库,并设计数据库中的表
| 表名 | 字段1 | 字段2 | 字段3 | 字段4 | 字段5 |
|---|---|---|---|---|---|
| user | id | name | password | locked | enable |
| roles | role_id | role_name | |||
| permissions | permissions_id | permissions_name |
有了这三张表还不够还要用另外三种表来表示者两张表直接的关系
- user<=>roles 赋予用户一个角色或多个角色
| 表名 | 字段1 | 字段2 |
|---|---|---|
| user_roles | user_id | role_id |
- roles<=>permissions 赋予角色一个权限或多个权限
| 表名 | 字段1 | 字段2 |
|---|---|---|
| roles_permissions | role_id | permissions_id |
关系表中的数据最好做一个外键,这样可以更好的处理数据。还有user表中的密码不要明文,要使用PasswordEncoder加盐。
2.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
3.连接数据库,在application.yml配置。我比较喜欢使用yml文件配置,所以以后的配置我都会使用yml文件格式。
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/steamapp?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
username: root
password: 123456
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
4.工具类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CustomAuthority implements GrantedAuthority {
private String authority;
@Override
public String getAuthority() {
return authority;
}
}
5.user、roles、permissions的实现类
@Data
public class User {
private String id;
private String name;
private String password;
private String locked;
private String enable;
}
@Data
public class Roles {
private String roleId;
private String roleName;
}
@Data
public class Permissions {
private String permissionId;
private String permissionName;
}
6.Service层、Mapper层
//Service层
@Service
public class UserService extends BaseService<User> {
@Autowired
private UserMapper userMapper;
@Override
public User getOne(Wrapper<User> queryWrapper) {
return userMapper.selectOne(queryWrapper);
}
}
BaseService类是自己写的工具类用来继承IService
public class BaseService<T> implements IService<T> {
@Override
public boolean saveBatch(Collection<T> entityList, int batchSize) {
return false;
}
@Override
public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
return false;
}
@Override
public boolean updateBatchById(Collection<T> entityList, int batchSize) {
return false;
}
@Override
public boolean saveOrUpdate(T entity) {
return false;
}
@Override
public T getOne(Wrapper<T> queryWrapper, boolean throwEx) {
return null;
}
@Override
public Map<String, Object> getMap(Wrapper<T> queryWrapper) {
return null;
}
@Override
public <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
return null;
}
@Override
public BaseMapper<T> getBaseMapper() {
return null;
}
@Override
public Class<T> getEntityClass() {
return null;
}
}
//Mapper层
@Repository
public interface UserMapper extends BaseMapper<User> {
}
@Repository
public interface PermissionsMapper extends BaseMapper<Roles> {
@Select("SELECT p.permission_id , p.permission_name from permissions p " +
"LEFT JOIN role_parmissions rp on rp.permission_id = p.permission_id " +
"LEFT JOIN roles r on r.role_id = rp.role_id " +
"LEFT JOIN user_roles ur on ur.role_id = r.role_id " +
"LEFT JOIN user u on u.id = ur.user_id " +
"WHERE u.id = #{userId}")
List<Permissions> FindAllPermissionFromUser(String userId);
}
7.插件主体
@Configuration
@MapperScan("com.java.mapper")
public class MybatisPlusConfig {
}
8.重写UserDetailsService
@Configuration
public class LoginService implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getName, username));
if (Objects.isNull(user)){
throw new UsernameNotFoundException("用户名不存在");
}
List<Permissions> permissions = rolesMapper.FindAllPermissionFromUser(user.getId());
List<CustomAuthority> newPermission = permissions.stream().map(permission -> new CustomAuthority(permission.getPermissionName())).collect(Collectors.toList());
return new LoginUser(user,newPermission);
}
}
我们要重写了UserDetailsService的loadUserByUsername方法来实现从数据库中获取数据。其中,因为loadUserByUsername方法的返回值是一个UserDetails类型所以我们就自己创建一个LoginUser实现UserDetails。
LoginUser.java
继承了UserDetails类
UserDetails是一个接口,定义了Spring Security在验证用户时所需的一些基本属性。该接口包含以下几个方法:
getUsername(): 返回用户名。
getPassword(): 返回用户密码。
getAuthorities(): 返回用户权限的集合。
isEnabled(): 返回用户是否已启用。
isAccountNonLocked(): 返回用户是否已解锁。
isAccountNonExpired(): 返回用户是否未过期。
isCredentialsNonExpired(): 返回用户的凭据是否未过期。
使用UserDetails接口,开发人员可以自定义用户对象,并在系统登录时进行身份验证。
通常情况下,开发人员会将UserDetails实现类在Spring Security的配置文件中进行配置,以便在身份认证过程中使用。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser implements UserDetails {
private User user;
private List<CustomAuthority> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
//return permissions.stream().map(permission -> new SimpleGrantedAuthority(permission.getPermissionName())).collect(Collectors.toList());
return authorities;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getName();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return user.getLocked();
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return user.getEnable();
}
}
9.将配置类中的UserDetailsService方法删除
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
通过案例的实现,我们就可以从数据库获取信息了。
结语
在大部分的项目设计种,登录时的数据基本都是从数据库获取的,所以第三种方式是非常常用的。并且我们使用的是Spring Security默认的方式进行的登录。以后我们会通过自定义的方式来认证、授权。