OAuth2.0实战(三)用户信息加载

简介: OAuth2.0实战(三)用户信息加载

Spring Security内置了三种用户存储方式:

1、基于内存

2、基于数据库查询语句

3、自定义UserDetailsService服务来获取

这里的用户存储指的是,从哪里获取用户的信息


1、基于内存存储用户信息


在WebSecurityConfig类中配置:


@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
    auth.inMemoryAuthentication()
            .withUser("user").password("{noop}123456").roles("USER")
            .and()
            .withUser("admin").password("{noop}123456").roles("USER","ADMIN");
}


withUser()方法返回的是UserDetailsManagerConfigurer.UserDetailsBuilder,这个对象提供了多个进一步配置用户的方法,如上面的为用户设置密码的password()方法、授予用户多个角色权限的roles()方法。UserDetailsManagerConfigurer.UserDetailsBuilder对象的更多方法如下表:

45.png


roles()方法是authorities()方法的简写形式。roles()方法所给定的值都会添加一个“ROLE_”前缀,并将其作为权限授予给用户。如下的配置和上面的等价:


auth.inMemoryAuthentication()
.withUser("user").password("{noop}password").authorities("ROLE_USER")
.and()
.withUser("admin").password("{noop}password").authorities("ROLE_USER","ROLE_ADMIN");


2.基于数据库库sql查询语句


auth
      .jdbcAuthentication()
         .dataSource(dataSource)
            .usersByUsernameQuery("select username,password,true from Spitter where username=?")
            .authoritiesByUsernameQuery("select username,'ROLE_USER' from Spitter where username=?");


3、自定义UserDetailsService服务来获取用户信息


@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(MyUserDetailsService());
}

UserDetailsService在身份认证中的作用

Spring Security中进行身份验证的是AuthenticationManager接口,ProviderManager是它的一个默认实现,但它并不用来处理身份认证,而是委托给配置好的AuthenticationProvider,每个AuthenticationProvider会轮流检查身份认证。检查后或者返回Authentication对象或者抛出异常。

验证身份就是加载响应的UserDetails,看看是否和用户输入的账号、密码、权限等信息匹配。此步骤由实现AuthenticationProvider的DaoAuthenticationProvider(它利用UserDetailsService验证用户名、密码和授权)处理。包含 GrantedAuthority 的 UserDetails对象在构建 Authentication对象时填入数据。

46.png


在Security中,角色和权限共用GrantedAuthority接口,唯一的不同角色就是多了个前缀"ROLE_",而且它没有Shiro的那种从属关系,即一个角色包含哪些权限等等。在Security看来角色和权限时一样的,它认证的时候,把所有权限(角色、权限)都取出来,而不是分开验证。

所以,在Security提供的UserDetailsService默认实现JdbcDaoImpl中,角色和权限都存储在auhtorities表中。而不是像Shiro那样,角色有个roles表,权限有个permissions表。以及相关的管理表等等。

GrantedAuthority接口的默认实现SimpleGrantedAuthority


注意,在构建SimpleGrantedAuthority对象的时候,它没有添加任何前缀。所以表示"角色"的权限,在数据库中就带有"ROLE_"前缀了。所以authorities表中的视图可能是这样的。

47.png


角色和权限能否分开存储?角色能不能不带"ROLE_"前缀

当然可以分开存储,你可以定义两张表,一张存角色,一张存权限。但是你自定义UserDetailsService的时候,需要保证把这两张表的数据都取出来,放到UserDails的权限集合中。当然你数据库中存储的角色也可以不带"ROLE_"前缀,就像这样。

48.png


49.png

但是前面说到了,Security才不管你是角色,还是权限。它只比对字符串。

比如它有个表达式hasRole(“ADMIN”)。那它实际上查询的是用户权限集合中是否存在字符串"ROLE_ADMIN"。如果你从角色表中取出用户所拥有的角色时不加上"ROLE_"前缀,那验证的时候就匹配不上了。

所以角色信息存储的时候可以没有"ROLE_"前缀,但是包装成GrantedAuthority对象的时候必须要有。


// 设置添加用户信息,正常应该从数据库中读取


@Bean
    UserDetailsService userDetailsService() {
       //内存保存
/*      InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
        userDetailsService.createUser(User.withUsername("user1").password("{noop}123456")
                .authorities("ROLE_USER").build());
        userDetailsService.createUser(User.withUsername("user2").password("{noop}1234567")
                .authorities("ROLE_USER").build());*/
        //数据库加载    
        UserDetailsService userDetailsService = new UserDetailsService(){
            //根据username加载用户基本信息,权限信息,角色信息   校验可以在自定义的daoAuhthenticationProvider中做
            @Override
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                AuthUser authUser = authUserMapper.selectByUsername(username);
                if (authUser == null) {
                    throw new UsernameNotFoundException("账户不存在");
                }
               // MyUserDetails myUserDetails = new MyUserDetails(authUser) ;
                //校验    这里只加载用户信息,尽量不要做校验   校验可以在自定义的daoAuhthenticationProvider中做
         /*       if (myUserDetails==null || myUserDetails.getAuthorities().isEmpty()) {
                    throw new UsernameNotFoundException("用户未分配任何权限");
                }*/
                return new User(authUser.getUsername(),authUser.getPassword(),this.getAuthorities(authUser));
            }
            public Collection<GrantedAuthority> getAuthorities(AuthUser authUser) {
                Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
                //查用户角色
                List<AuthRole> authRoles = authRoleMapper.selectByUserId(authUser.getId());
                //查用户权限
                List<AuthPermission> authPermissions = authPermissionMapper.selectByRoles(authRoles);
                if (authRoles != null)
                {
                    //角色权限
                    Set<String> roleStrs =   authRoles.stream().map(AuthRole::getRoleName).collect(Collectors.toSet());
                    for (String code : roleStrs) {
                        SimpleGrantedAuthority authority = new SimpleGrantedAuthority(code);
                        authorities.add(authority);
                    }
                    //方法权限
                    Set<String> authStrs =   authPermissions.stream().map(AuthPermission::getCode).collect(Collectors.toSet());
                    for (String code : authStrs) {
                        SimpleGrantedAuthority authority = new SimpleGrantedAuthority(code);
                        authorities.add(authority);
                    }
                }
                return authorities;
            }
        };
        return userDetailsService;
    }


说明:

这里的返回结果User就是UserDetails接口的实现类:

public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
    this(username, password, true, true, true, true, authorities);
}


构造方案非常简单:

username 用户名

password 密码

authorities 权限


总结:

1、本章介绍了OAuth2.0用户信息可以有2种存储方式,一种是存放在内存中,一般只会在测试时使用;一种是基于数据库的持久化存储。


三种加载用户信息的方式:


直接基于内存中的用户信息去进行认证inMemoryAuthentication

采用sql语句从数据库加载用户信息

继承UserDetailsService方法,加载用户信息,推荐使用这种方法,最灵活通用。


github源码:

https://github.com/StarlightWANLI/oauth2.git

目录
相关文章
|
10月前
|
JSON 安全 Java
什么是JWT?如何使用Spring Boot Security实现它?
什么是JWT?如何使用Spring Boot Security实现它?
1778 5
|
7月前
|
负载均衡 Java Nacos
Spring Cloud五大组件
Spring Cloud五大组件
|
12月前
|
人工智能 测试技术 数据处理
首个Mamba+Transformer混合架构多模态大模型来了,实现单卡千图推理
【10月更文挑战第18天】《LongLLaVA: Scaling Multi-modal LLMs to 1000 Images Efficiently via Hybrid Architecture》提出了一种新型多模态大模型LongLLaVA,结合了Mamba和Transformer架构,通过系统优化实现在单张A100 80GB GPU上处理近千张图像的突破。该模型在视频理解、高分辨率图像分析和多模态智能体任务中表现出色,显著提升了计算效率。
401 65
|
11月前
|
数据安全/隐私保护
OAuth2.0实战案例
OAuth2.0实战案例
266 0
OAuth2.0实战案例
|
11月前
|
消息中间件 Ubuntu Java
Ubuntu系统上安装Apache Kafka
Ubuntu系统上安装Apache Kafka
|
缓存 NoSQL Java
在 Spring Boot 应用中使用 Spring Cache 和 Redis 实现数据查询的缓存功能
在 Spring Boot 应用中使用 Spring Cache 和 Redis 实现数据查询的缓存功能
703 0
|
消息中间件 JSON Java
Spring Boot、Spring Cloud与Spring Cloud Alibaba版本对应关系
Spring Boot、Spring Cloud与Spring Cloud Alibaba版本对应关系
24344 0
|
计算机视觉
detectMultiScale
【6月更文挑战第8天】
704 4
|
消息中间件 数据库 RocketMQ
支付回调处理流程分析|学习笔记
快速学习支付回调处理流程分析
733 0
支付回调处理流程分析|学习笔记
|
缓存 搜索推荐 算法
Java排序实战:如何高效实现电商产品排序
在当今的数字化时代,电子商务已成为人们日常生活的重要组成部分。消费者可以在电商平台上浏览和购买来自全球的商品,这无疑为我们的生活带来了极大的便利。然而,随着电商平台的规模不断扩大,商品数量的急剧增加,如何对海量商品进行高效排序成为了电商系统开发的一大挑战。