上一章已經把表結構上傳了,今天這部分主要用到的表是
- SYS_USERS用戶管理表
- SYS_ROLES角色管理表
- SYS_AUTHORITIES權限管理表
- SYS_USERS_ROLES用戶角色表
- SYS_ROLES_AUTHORITIES角色權限表
要實現使用數據庫管理用戶,需要自定義用戶登錄功能,而Spring已經為我們提供了接口UserDetailsService
~~~
package org.springframework.security.core.userdetails;
public interface UserDetailsService {
/**
* Locates the user based on the username. In the actual implementation, the search may possibly be case
* insensitive, or case insensitive depending on how the implementation instance is configured. In this case, the
* <code>UserDetails</code> object that comes back may have a username that is of a different case than what was
* actually requested..
*
* @param username the username identifying the user whose data is required.
*
* @return a fully populated user record (never <code>null</code>)
*
* @throws UsernameNotFoundException if the user could not be found or the user has no GrantedAuthority
*/
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
~~~
UserDetailsService是一個接口,只有一個方法loadUserByUsername,根據方法名可以看出這個方法是根據用戶名來獲取用戶信息,但是返回的是一個UserDetails對象。而UserDetails也是一個接口
~~~
package org.springframework.security.core.userdetails;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import java.io.Serializable;
import java.util.Collection;
//這里省略了Spring的注釋,只是我自己對這些方法的簡單的注釋,如果想了解Spring對這些方法的注釋,請查看Spring源碼
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities(); //權限集合
String getPassword(); //密碼
String getUsername(); //用戶名
boolean isAccountNonExpired(); //賬戶沒有過期
boolean isAccountNonLocked(); //賬戶沒有被鎖定
boolean isCredentialsNonExpired(); //證書沒有過期
boolean isEnabled();//賬戶是否有效
}
~~~
因此我們的SysUsers這個bean需要實現這個接口
~~~
@Entity
@DynamicUpdate(true)
@DynamicInsert(true)
@Table(name = "SYS_USERS", schema = "FYBJ")
public class SysUsers implements UserDetails,Serializable {
/**
*
*/
private static final long serialVersionUID = -6498309642739707784L;
// Fields
private String userId;
private String username;
private String name;
private String password;
private Date dtCreate;
private Date lastLogin;
private Date deadline;
private String loginIp;
private String VQzjgid;
private String VQzjgmc;
private String depId;
private String depName;
private boolean enabled;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
@JsonIgnore
private Set<SysUsersRoles> sysUsersRoleses = new HashSet<SysUsersRoles>(0);
private Collection<GrantedAuthority> authorities;
//.....省略setter,getter.....
//如果屬性是boolean(注:不是Boolean)類型的值,在生產getter時會變為isXxx,如enabled生產getter為isEnabled
}
~~~
這樣寫我們的SysUsers只要生產getter和setter方法就實現了UserDetails,同時還可以使用數據庫來控制這些屬性,兩全其美。
在UserDetails中有個屬性需要注意下Collection<GrantedAuthority> ?authorities,這個屬性中存儲了這個用戶所有的權限。
下面需要先寫下SysUsers的DAO層,一個方法是根據用戶名獲取用戶,一個方法是根據用戶名獲取用戶所有的權限,這里我用的是Spring Data Jpa,如果不懂這個請自行從網上查閱資料
~~~
public interface SysUsersRepository extends JpaRepository<SysUsers, String> {
public SysUsers getByUsername(String username);
public Collection<GrantedAuthority> loadUserAuthorities(String username);
}
~~~
其中getByUsername符合Spring的命名規范,所以這個方法不需要我們來實現,而loadUserAuthorities則需要我們自己動手實現
~~~
public class SysUsersRepositoryImpl {
protected Log logger = LogFactory.getLog(getClass());
@PersistenceContext
private EntityManager entityManager;
/**
* 根據用戶名獲取到用戶的權限并封裝成GrantedAuthority集合
* @param username
*/
public Collection<GrantedAuthority> loadUserAuthorities(String username){
List<SysAuthorities> list = this.getSysAuthoritiesByUsername(username);
List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
for (SysAuthorities authority : list) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(authority.getAuthorityMark());
auths.add(grantedAuthority);
}
return auths;
}
/**
* 先根據用戶名獲取到SysAuthorities集合
* @param username
* @return
*/
@SuppressWarnings("unchecked")
private List<SysAuthorities> getSysAuthoritiesByUsername(String username){
String sql = "SELECT * FROM SYS_AUTHORITIES WHERE AUTHORITY_ID IN( "+
"SELECT DISTINCT AUTHORITY_ID FROM SYS_ROLES_AUTHORITIES S1 "+
"JOIN SYS_USERS_ROLES S2 ON S1.ROLE_ID = S2.ROLE_ID "+
"JOIN SYS_USERS S3 ON S3.USER_ID = S2.USER_ID AND S3.USERNAME=?1)";
Query query = this.entityManager.createNativeQuery(sql, SysAuthorities.class);
query.setParameter(1, username);
List<SysAuthorities> list = query.getResultList();
return list;
}
}
~~~
不管是用Spring Data Jpa還是普通的方法只要實現這兩個方法就可以了
最后也是最重要的一個類UserDetailsService
~~~
public class DefaultUserDetailsService implements UserDetailsService {
protected final Log logger = LogFactory.getLog(getClass());
@Autowired
private SysUsersRepository sysUsersRepository;
@Autowired
private MessageSource messageSource;
@Autowired
private UserCache userCache;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
SysUsers user = (SysUsers) this.userCache.getUserFromCache(username);
if(user == null){
user = this.sysUsersRepository.getByUsername(username);
if(user == null)
throw new UsernameNotFoundException(this.messageSource.getMessage(
"UserDetailsService.userNotFount", new Object[]{username}, null));
//得到用戶的權限
auths = this.sysUsersRepository.loadUserAuthorities( username );
user.setAuthorities(auths);
}
logger.info("***********"+username+"*************");
logger.info(user.getAuthorities());
logger.info("****************************");
this.userCache.putUserInCache(user);
return user;
}
}
~~~
在loadUserByUsername方法中首先是從緩存中查找用戶,如果找到用戶就直接用緩存中的用戶,如果沒有找到就從數據庫中獲取用戶信息。
從數據庫中獲取用戶時先獲取User對象,如果用戶為空則拋出UsernameNotFoundException,其中UserDetailsService.userNotFount是在property文件中自定義的,如果獲取到了user則再獲取用戶的權限,按照Spring的標準如果沒有任何權限也是要拋出這個異常的,在這里我們就不做判斷了。
登錄后可以看到控制臺打印出來以下信息
~~~
***********admin*************
[AUTH_PASSWORD_MODIFY, AUTH_GG_FBGBGG, AUTH_GG_FBZNGG]
****************************
~~~
說明我們登錄成功并且已經獲取到了權限,但是可能會出現如下頁面

這樣就是你在數據庫中存儲的權限跟配置文件中的不對應,或者說訪問資源是沒有從用戶的權限集合中找到這個權限。
- 前言
- (大綱)----學習過程分享
- (1)----SpringSecurity3.2環境搭建
- (2)----SpringSecurity簡單測試
- (3)---- 自定義登錄頁面
- (4)---- 數據庫表結構的創建
- (5)---- 國際化配置及UserCache
- (6)---- 使用數據庫管理用戶及權限
- (7)---- 解決UsernameNotFoundException無法被捕獲的問題
- (8)---- 自定義決策管理器及修改權限前綴
- (9)---- 自定義AccessDeniedHandler
- (10)---- 自定義登錄成功后的處理程序及修改默認驗證地址
- (11)---- 使用數據庫來管理資源
- (12)---- 使用數據庫來管理方法
- (13)---- 驗證碼功能的實現
- (14)---- Logout和SessionManager