天天看點

SpringBoot整合Spring Security(三) ——從資料庫擷取使用者名密碼通過資料庫擷取使用者名密碼自定義使用者類

通過資料庫擷取使用者名密碼

通常來講。企業的管理系統使用者的資訊都是儲存在資料庫中的。是以我們要在登入的時候從資料庫提取使用者的使用者名和密碼。

何況,在在代碼中維護使用者資訊也不現實,讓人家運維怎麼想。

那麼接下來配置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"。這裡隻是暫時意思一下。

在這個類中也可以加入一些使用者昵稱等自定義資訊,如此這般就完成了高度的自定義化。

最後來看一下現在完整的檔案結構

SpringBoot整合Spring Security(三) ——從資料庫擷取使用者名密碼通過資料庫擷取使用者名密碼自定義使用者類