1 Star 0 Fork 552

Leader / CS-Wiki

forked from 小牛肉 / cs-wiki 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
4-基于内存数据库的认证和授权.md 5.63 KB
一键复制 编辑 原始数据 按行查看 历史
小牛肉 提交于 2021-07-11 21:36 . 🥽 更新目录结构

🥛 基于内存数据库的身份认证和角色授权


在上节我们使用基于内存的方式体验了下 Spring Security,在实际项目中,都是需要数据库进行操作的,本节使用 hsqldb 内存数据库进行说明。

这里我们使用 Spring Data JPA 操作数据库,需要一个用户的实体类,再者需要定义相应的 Service 获取用户的信息,最后重写 UserDetailsServiceloadUserByUsername 方法从数据库中获取用户信息,传给 Spring Security 进行处理。

1. 添加依赖

添加 SpringData JPAhsqldb 的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <scope>runtime</scope>
</dependency>

2. 配置类

@Configuration
@EnableWebSecurity // 开启Spring Security的功能
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

和上节一样,只不过我们不需要实现认证和授权方法。

3. 创建实体类

创建 UserInfo 实体类:

@Entity
public class UserInfo {

    public enum Role{
        admin, normal
    }
	
    @Id // 主键
    @GeneratedValue // 自增
    private long uid;

    private String username;
    private String password;

    @Enumerated(EnumType.STRING)
    private Role role;

    // Getter And Setter
}

🔺 这里我们将 Role 写死了,后面会将 Role 作为一张数据库表进行动态加载

4. 创建 Repository(Dao)

创建和数据库交互的 UserInfoRepository

public interface UserInfoRepository extends JpaRepository<UserInfo,Long> {

    public UserInfo findByUsername(String username);

}

5. 创建 Service

创建 UserInfoService

public interface UserInfoService {

    public UserInfo findByUsername(String username);

}

创建 UserInfoService 的实现类:

@Service
public class UserInfoServiceImpl implements UserInfoService {

    @Autowired
    private UserInfoRepository userInfoRepository;

    @Override
    public UserInfo findByUsername(String username) {
        return userInfoRepository.findByUsername(username);
    }

}

6. 自定义 UserDetailsService

🚩 我们需要继承一个 UserDetailSevice 接口并且加入到容器中,实现 loadUserByUsername 方法,里面的逻辑通常是从数据库查找出对应用户名的密码然后构造一个 UserDetail 对象,Spring Security会根据返回的这个带有正确用户信息的对象和前台传过来的用户名密码进行比对来判断是否认证通过。

实现 UserDetailsService 类的 loadUserByUsername 方法:

import org.springframework.security.core.userdetails.UserDetailsService;

......

@Service
public class CustomUserDetailService implements UserDetailsService {

    @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("CustomUserDetailService.loadUserByUsername()");
        // 通过 username 获取用户信息
        UserInfo userInfo = userInfoService.findByUsername(username);
        if(userInfo == null)
            throw new UsernameNotFoundException("not found");


        // 定义权限列表
        List<GrantedAuthority> authorities = new ArrayList<>();
        // 用户所拥有的权限 必须以 ROLE_ 开头
        authorities.add(new SimpleGrantedAuthority("ROLE_" + userInfo.getRole().name()));

        // 这里的密码需要使用PasswordEncoder进行加密,否则会报错
        User userDetails = new User(userInfo.getUsername(), passwordEncoder.encode(userInfo.getPassword()), authorities);
        return userDetails;
    }
}

7. Controller

和上节一样:

@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String hello() {
        return "Hello,Spring Security";
    }

    @RequestMapping("/helloAdmin")
    @PreAuthorize("hasAnyRole('admin')") // 只允许角色 admin 访问
    public String helloAdmin() {
        return "Hello,admin";
    }

    @RequestMapping("/helloUser")
    @PreAuthorize("hasAnyRole('admin','normal')") // 允许角色 admin 和 normal 访问
    public String helloUser() {
        return "Hello,user";
    }
}

8. 初始化测试账号

这里我们使用一个 DataInit 类,初始化两个账号admin/123和user/123:

@Service
public class DataInit {

    @Autowired
    UserInfoRepository userInfoRepository;

    @PostConstruct
    public void dataInit(){

        // 用户 admin
        UserInfo admin = new UserInfo();
        admin.setUsername("admin");
        admin.setPassword("123");
        admin.setRole(UserInfo.Role.admin);
        userInfoRepository.save(admin);

        // 用户 user
        UserInfo user = new UserInfo();
        user.setUsername("user");
        user.setPassword("123");
        user.setRole(UserInfo.Role.normal);
        userInfoRepository.save(user);
    }
}

9. 测试

测试账号:admin/123,上面的两个地址应该都是可以正常访问的。

测试账号:user/123,只能访问 http://127.0.0.1:8080/helloUser

📚 References

Java
1
https://gitee.com/mmbuy_admin/CS-Wiki.git
git@gitee.com:mmbuy_admin/CS-Wiki.git
mmbuy_admin
CS-Wiki
CS-Wiki
master

搜索帮助