Spring Security 用戶信息數(shù)據(jù)源

前面章節(jié)中用戶名、密碼、權(quán)限都是寫(xiě)在配置文件里的,不能動(dòng)態(tài)的管理用戶的權(quán)限,大多數(shù)時(shí)候顯然是不行的。這里介紹從其他數(shù)據(jù)源讀取用戶的信息,例如從數(shù)據(jù)庫(kù),LDAP 等。只需要給 authentication-provider 提供接口 UserDetailsService 的實(shí)現(xiàn)類即可,使用這個(gè)類獲取用戶的信息,涉及以下內(nèi)容:
  • 修改 spring-security.xml 中的 authentication-provider
  • 類 UserDetailsService 實(shí)現(xiàn)了 Spring Security 的接口 UserDetailsService
  • 類 User
  • 類 UserService
spring-security.xml

修改 authentication-manager 下的 user-service-ref 為我們自定義的 UserDetailsService

    
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
        xmlns="http://www.springframework.org/schema/security"
        xmlns:beans="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/security
            http://www.springframework.org/schema/security/spring-security.xsd">
    <beans:bean id="loginSuccessHandler" class="com.xtuer.security.LoginSuccessHandler"/>
    <http security="none" pattern="/static/**"/>
    <http auto-config="true">
        <intercept-url pattern="/admin" access="hasRole('ADMIN')"/>
        <intercept-url pattern="/login" access="permitAll"/>
        <form-login login-page="/login"
                    login-processing-url="/login"
                    default-target-url  ="/"
                    authentication-success-handler-ref="loginSuccessHandler"
                    authentication-failure-url="/login?error"
                    username-parameter="username"
                    password-parameter="password"/>
        <access-denied-handler error-page="/deny" />
        <logout logout-url="/logout" logout-success-url="/login?logout" />
        <csrf disabled="true"/>
    </http>
    <beans:bean id="userDetailsService" class="com.xtuer.security.UserDetailsService"/>
    <authentication-manager>
        <authentication-provider user-service-ref="userDetailsService"/>
    </authentication-manager>
</beans:beans>
UserDetailsService
UserDetailsService 的作用是根據(jù)登錄表單中用戶的用戶名查找用戶信息用于身份認(rèn)證。Spring Security 中授權(quán)分 2 步:身份驗(yàn)證 (Authentication),權(quán)限驗(yàn)證 (Authorization)
  • 用戶在登錄頁(yè)面輸入 username, password,然后點(diǎn)擊登錄按鈕
  • 方法 loadUserByUsername() 使用 username 查找到用戶的信息,如密碼,權(quán)限等
  • Spring Security 使用查找到的密碼和加密后用戶輸入的密碼進(jìn)行比較,如果相等,則身份驗(yàn)證成功
  • 身份驗(yàn)證成功后,使用用戶的權(quán)限和頁(yè)面的訪問(wèn)權(quán)限比較,頁(yè)面的權(quán)限配置在 intercept-url

    
package com.xtuer.security;
import com.xtuer.bean.User;
import com.xtuer.service.UserService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
    private UserService userService = new UserService();
    /**
     * 使用 username 加載用戶的信息,如密碼,權(quán)限等
     * @param  username 登陸表單中用戶輸入的用戶名
     * @return 返回查找到的用戶對(duì)象
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.findUserByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException(username + " not found!");
        }
        return user;
    }
}
UserService
UserService 用戶查找用戶信息
    
package com.xtuer.service;
import com.xtuer.bean.User;
import java.util.HashMap;
import java.util.Map;
public class UserService {
    private static Map<String, User> users = new HashMap<String, User>();
    static {
        // 模擬數(shù)據(jù)源,可以是多種,如數(shù)據(jù)庫(kù),LDAP,從配置文件讀取等
        users.put("admin", new User("admin", "{noop}Passw0rd", "ROLE_ADMIN"));
        users.put("alice", new User("alice", "{noop}Passw0rd", "ROLE_USER"));
    }
    public User findUserByUsername(String username) {
        return users.get(username);
    }
}

User

    
package com.xtuer.bean;
import com.alibaba.fastjson.JSON;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import java.util.*;
/**
 * 用戶類型,根據(jù) userdetails.User 的設(shè)計(jì),roles, authorities, expired 等狀態(tài)不能修改,
 * 只能是創(chuàng)建用戶對(duì)象的時(shí)候傳入進(jìn)來(lái)。
 */
@Getter
@Setter
public class User extends org.springframework.security.core.userdetails.User {
    private Long   id;
    private String username;
    private String password;
    private String mail;
    private boolean enabled;
    private Set<String> roles = new HashSet<>(); // 用戶的角色
    public User() {
        // 父類不允許空的用戶名、密碼和權(quán)限,所以給個(gè)默認(rèn)的,這樣就可以用默認(rèn)的構(gòu)造函數(shù)創(chuàng)建 User 對(duì)象。
        super("non-exist-username", "", new HashSet<>());
    }
    /**
     * 使用賬號(hào)、密碼、角色創(chuàng)建用戶
     *
     * @param username 賬號(hào)
     * @param password 密碼
     * @param roles    角色
     */
    public User(String username, String password, String... roles) {
        this(username, password, true, roles);
    }
    /**
     * 使用賬號(hào)、密碼、是否禁用、角色創(chuàng)建用戶
     *
     * @param username 賬號(hào)
     * @param password 密碼
     * @param enabled  是否禁用
     * @param roles    角色
     */
    public User(String username, String password, boolean enabled, String... roles) {
        super(username, password, enabled, true, true, true, AuthorityUtils.createAuthorityList(roles));
        this.username = username;
        this.password = password;
        this.enabled  = enabled;
        this.roles.addAll(Arrays.asList(roles));
    }
    /**
     * 用戶信息修改后,例如角色修改后不會(huì)更新到父類的 authorities 中,需要重新創(chuàng)建一個(gè)用戶對(duì)象才行
     *
     * @param user 已有用戶對(duì)象
     * @return 新的用戶對(duì)象,權(quán)限等信息更新到了父類的 authorities 中
     */
    public static User userForSpringSecurity(User user) {
        return new User(user.username, user.password, user.enabled, user.getRoles().toArray(new String[0]));
    }
    public static void main(String[] args) {
        User user1 = new User();
        System.out.println(JSON.toJSONString(user1));
        System.out.println(user1.getRoles());
        User user2 = new User("Bob", "Passw0rd", "ROLE_USER", "ROLE_ADMIN");
        System.out.println(JSON.toJSONString(user2));
        System.out.println(user2.getRoles());
    }
}

測(cè)試

訪問(wèn) http://localhost:8080/hello
訪問(wèn) http://localhost:8080/admin
輸入錯(cuò)誤的用戶名或密碼,觀察登陸失敗的頁(yè)面
輸入正確的用戶名和密碼,繼續(xù)登陸
訪問(wèn) http://localhost:8080/logout,觀察注銷成功的頁(yè)面
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容