Spring Security 学习笔记二

147 阅读4分钟

设置登录系统的账户、密码

方法一、配置文件

在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
useridnamepasswordlockedenable
rolesrole_idrole_name
permissionspermissions_idpermissions_name

有了这三张表还不够还要用另外三种表来表示者两张表直接的关系

  • user<=>roles 赋予用户一个角色或多个角色
表名字段1字段2
user_rolesuser_idrole_id
  • roles<=>permissions 赋予角色一个权限或多个权限
表名字段1字段2
roles_permissionsrole_idpermissions_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);
    }
}
​
​

我们要重写了UserDetailsServiceloadUserByUsername方法来实现从数据库中获取数据。其中,因为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默认的方式进行的登录。以后我们会通过自定义的方式来认证、授权。