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對象的更多方法如下表:
在這裏插入圖片描述

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對象時填入數據。
在這裏插入圖片描述

在Security中,角色和權限共用GrantedAuthority接口,唯一的不同角色就是多了個前綴"ROLE_",而且它沒有Shiro的那種從屬關係,即一個角色包含哪些權限等等。在Security看來角色和權限時一樣的,它認證的時候,把所有權限(角色、權限)都取出來,而不是分開驗證。
所以,在Security提供的UserDetailsService默認實現JdbcDaoImpl中,角色和權限都存儲在auhtorities表中。而不是像Shiro那樣,角色有個roles表,權限有個permissions表。以及相關的管理表等等。
GrantedAuthority接口的默認實現SimpleGrantedAuthority

注意,在構建SimpleGrantedAuthority對象的時候,它沒有添加任何前綴。所以表示"角色"的權限,在數據庫中就帶有"ROLE_"前綴了。所以authorities表中的視圖可能是這樣的。
在這裏插入圖片描述

角色和權限能否分開存儲?角色能不能不帶"ROLE_"前綴
當然可以分開存儲,你可以定義兩張表,一張存角色,一張存權限。但是你自定義UserDetailsService的時候,需要保證把這兩張表的數據都取出來,放到UserDails的權限集合中。當然你數據庫中存儲的角色也可以不帶"ROLE_"前綴,就像這樣。
在這裏插入圖片描述
在這裏插入圖片描述

但是前面說到了,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

如果你還有其他OAuth2.0認證方面的想了解的內容,可以在底下評論留言。我會盡力抽空調研下。

最後,如果我寫的文章對您能有些許幫助,請幫忙點贊、關注。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章