通過資料庫擷取使用者名密碼
通常來講。企業的管理系統使用者的資訊都是儲存在資料庫中的。是以我們要在登入的時候從資料庫提取使用者的使用者名和密碼。
何況,在在代碼中維護使用者資訊也不現實,讓人家運維怎麼想。
那麼接下來配置Spring Security使使用者登入的時候從資料庫提取資料進行比對。
package com.example.demo.config.springSecurity;
import com.example.demo.dao.SysUserDao;
import com.example.demo.entity.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class SecurityUserDetailsService implements UserDetailsService {
@Autowired
private SysUserDao dao;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
SysUser sysUser = dao.findSysUserByUsername(s);
User user = new User(sysUser.getUsername(), sysUser.getPassword(), AuthorityUtils.createAuthorityList("user"));
return user;
}
}
建立一個類對UserDetailsService接口進行實作。這裡主要是實作loadUserByUsername方法,這裡的參數s是前端傳過來的使用者名,可以在IDE建立完代碼後改成username友善閱讀代碼
其中User類是Spring Security預設的使用者類,它主要有2個構造方法
public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
this(username, password, true, true, true, true, authorities);
}
public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
Assert.isTrue(username != null && !"".equals(username) && password != null, "Cannot pass null or empty values to constructor");
this.username = username;
this.password = password;
this.enabled = enabled;
this.accountNonExpired = accountNonExpired;
this.credentialsNonExpired = credentialsNonExpired;
this.accountNonLocked = accountNonLocked;
this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
}
構造的參數依次為使用者名,密碼,賬戶是否可用,賬戶是否過期,憑證是否可用,賬戶是否鎖定以及權限集
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.dao.SysUserDao">
<select id="findSysUserByUsername" parameterType="String" resultType="SysUser">
select
id,
username,
password
from sys_user
where
username = #{username}
</select>
</mapper>
package com.example.demo.dao;
import com.example.demo.entity.SysUser;
import org.springframework.stereotype.Repository;
@Repository
public interface SysUserDao {
public SysUser findSysUserByUsername(String username);
}
package com.example.demo.entity;
public class SysUser{
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
在這裡,定義了一個SysUser去接收資料庫查詢的資料,就不多做解釋了。
還有别忘了注冊spring。
我這裡使用了@Service
自定義使用者類
這麼做有點重複,有點啰嗦了。那麼再來精簡一下代碼,自定義User類
首先改造SysUser類
package com.example.demo.entity;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Set;
public class SysUser implements UserDetails, CredentialsContainer {
private int id;
private String username;
private String password;
private boolean enabled;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
private Set<GrantedAuthority> authorities;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public boolean isAccountNonExpired() {
return accountNonExpired;
}
public void setAccountNonExpired(boolean accountNonExpired) {
this.accountNonExpired = accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return accountNonLocked;
}
public void setAccountNonLocked(boolean accountNonLocked) {
this.accountNonLocked = accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}
public void setCredentialsNonExpired(boolean credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
}
@Override
public Set<GrantedAuthority> getAuthorities() {
return authorities;
}
public void setAuthorities(Set<GrantedAuthority> authorities) {
this.authorities = authorities;
}
@Override
public void eraseCredentials() {
this.password = null;
}
}
直接讓SysUser實作UserDetails, CredentialsContainer。其中UserDetails是用來驗證的,CredentialsContainer是在驗證完了之後對使用者資訊中的密碼等敏感資訊進行擦除的。主要實作方法為eraseCredentials()
修改完之後的SecurityUserDetailsService
package com.example.demo.config.springSecurity;
import com.example.demo.dao.SysUserDao;
import com.example.demo.entity.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.HashSet;
@Service
public class SecurityUserDetailsService implements UserDetailsService {
@Autowired
private SysUserDao dao;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
SysUser sysUser = dao.findSysUserByUsername(s);
sysUser.setAuthorities(new HashSet<>(AuthorityUtils.createAuthorityList("admin")));
return sysUser;
}
}
以及sql
<select id="findSysUserByUsername" parameterType="String" resultType="SysUser">
select
id,
username,
password,
enabled,
account_non_expired,
credentials_non_expired,
account_non_locked
from sys_user
where
username = #{username}
</select>
其中sysUser.setAuthorities(new HashSet<>(AuthorityUtils.createAuthorityList(“admin”)));是鑒權的部分。這個會在後面講。當然并不是每個人都是"admin"。這裡隻是暫時意思一下。
在這個類中也可以加入一些使用者昵稱等自定義資訊,如此這般就完成了高度的自定義化。
最後來看一下現在完整的檔案結構
