Spring Boot+CAS 單點登陸,如何對接數據庫?

在前面的兩篇文章中,鬆哥和你們分享了 CAS Server 的搭建以及如何使用 Spring Security 搭建 CAS Client。java

可是前面的案例有一個問題,就是登陸用戶是在 CAS Server 配置文件中寫死的,沒有對接數據庫,實際項目中,這裏確定要對接數據庫,因此今天,鬆哥就來和你們聊一聊 CAS Server 如何對接數據庫。mysql

鬆哥最近和 Spring Security 槓上了,這是 Spring Security 系列的第 25 篇:git

  1. 挖一個大坑,Spring Security 開搞!
  2. 鬆哥手把手帶你入門 Spring Security,別再問密碼怎麼解密了
  3. 手把手教你定製 Spring Security 中的表單登陸
  4. Spring Security 作先後端分離,咱就別作頁面跳轉了!通通 JSON 交互
  5. Spring Security 中的受權操做原來這麼簡單
  6. Spring Security 如何將用戶數據存入數據庫?
  7. Spring Security+Spring Data Jpa 強強聯手,安全管理只有更簡單!
  8. Spring Boot + Spring Security 實現自動登陸功能
  9. Spring Boot 自動登陸,安全風險要怎麼控制?
  10. 在微服務項目中,Spring Security 比 Shiro 強在哪?
  11. SpringSecurity 自定義認證邏輯的兩種方式(高級玩法)
  12. Spring Security 中如何快速查看登陸用戶 IP 地址等信息?
  13. Spring Security 自動踢掉前一個登陸用戶,一個配置搞定!
  14. Spring Boot + Vue 先後端分離項目,如何踢掉已登陸用戶?
  15. Spring Security 自帶防火牆!你都不知道本身的系統有多安全!
  16. 什麼是會話固定攻擊?Spring Boot 中要如何防護會話固定攻擊?
  17. 集羣化部署,Spring Security 要如何處理 session 共享?
  18. 鬆哥手把手教你在 SpringBoot 中防護 CSRF 攻擊!so easy!
  19. 要學就學透徹!Spring Security 中 CSRF 防護源碼解析
  20. Spring Boot 中密碼加密的兩種姿式!
  21. Spring Security 要怎麼學?爲何必定要成體系的學習?
  22. Spring Security 兩種資源放行策略,千萬別用錯了!
  23. 聊一聊 Spring Boot 中的 CAS 單點登陸
  24. Spring Boot 實現單點登陸的第三種方案!

1.總體思路

先來看總體思路。github

咱們用 CAS Server 作單點登陸,CAS Server 主要是負責認證的,也就是它主要解決登陸問題。登陸成功以後,還有一個權限處理的問題,權限的問題則交由各個 CAS Client 自行處理,並不在 CAS Server 中完成。spring

在上篇文章中,鬆哥有教過你們定義 UserDetailsService,不知道你們是否還記得以下代碼(忘記了能夠參考上篇文章:Spring Boot 實現單點登陸的第三種方案!):sql

@Component
@Primary
public class UserDetailsServiceImpl implements UserDetailsService{

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        return new User(s, "123", true, true, true, true,
                AuthorityUtils.createAuthorityList("ROLE_user"));
    }
}

這段代碼是在何時執行呢?數據庫

若是咱們沒有使用 CAS 這一套的話,這段代碼固然是在用戶登陸的時候執行,用戶登陸時,從數據庫中查詢用戶的信息,而後作校驗(參考本系列前面文章就懂)。後端

若是咱們使用 CAS 這一套,用戶登陸的校驗將在 CAS Server 上執行,CAS Client 就不用作校驗工做了,可是爲何咱們還須要定義 UserDetailsService 呢?這是爲了當用戶在 CAS Server 上登陸成功以後,拿着用戶名回到 CAS Client,而後咱們再去數據庫中根據用戶名獲取用戶的詳細信息,包括用戶的角色等,進而在後面的鑑權中用上角色。瀏覽器

好了,這是咱們一個大體的思路,接下來咱們來看具體實現。安全

2.具體實現

接下來的配置在 鬆哥手把手教你入門 Spring Boot + CAS 單點登陸 一文的基礎上完成,因此還沒看前面文章的小夥伴建議先看一下哦。

2.1 準備工做

首先咱們先在數據庫中準備一下用戶表、角色表以及用戶角色關聯表:

CREATE TABLE `t_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `name_zh` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `t_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `account_non_expired` bit(1) NOT NULL,
  `account_non_locked` bit(1) NOT NULL,
  `credentials_non_expired` bit(1) NOT NULL,
  `enabled` bit(1) NOT NULL,
  `password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `t_user_roles` (
  `t_user_id` bigint(20) NOT NULL,
  `roles_id` bigint(20) NOT NULL,
  KEY `FKj47yp3hhtsoajht9793tbdrp4` (`roles_id`),
  KEY `FK7l00c7jb4804xlpmk1k26texy` (`t_user_id`),
  CONSTRAINT `FK7l00c7jb4804xlpmk1k26texy` FOREIGN KEY (`t_user_id`) REFERENCES `t_user` (`id`),
  CONSTRAINT `FKj47yp3hhtsoajht9793tbdrp4` FOREIGN KEY (`roles_id`) REFERENCES `t_role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO `t_role` (`id`, `name`, `name_zh`) VALUES (1,'ROLE_admin','管理員'),(2,'ROLE_user','普通用戶');
INSERT INTO `t_user` (`id`, `account_non_expired`, `account_non_locked`, `credentials_non_expired`, `enabled`, `password`, `username`) VALUES (1,b'1',b'1',b'1',b'1','123','javaboy'),(2,b'1',b'1',b'1',b'1','123','江南一點雨');
INSERT INTO `t_user_roles` (`t_user_id`, `roles_id`) VALUES (1,1),(2,2);

2.2 CAS Server

而後咱們要在 CAS Server 的 pom.xml 文件中添加兩個依賴:

<dependency>
    <groupId>org.apereo.cas</groupId>
    <artifactId>cas-server-support-jdbc-drivers</artifactId>
    <version>${cas.version}</version>
</dependency>
<dependency>
    <groupId>org.apereo.cas</groupId>
    <artifactId>cas-server-support-jdbc</artifactId>
    <version>${cas.version}</version>
</dependency>

注意這裏不用添加數據庫驅動,系統會自動解決。

添加完成以後,再在 src/main/resources/application.properties 文件中添加以下配置:

cas.authn.jdbc.query[0].url=jdbc:mysql://127.0.0.1:3306/withjpa?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false
cas.authn.jdbc.query[0].user=root
cas.authn.jdbc.query[0].password=123
cas.authn.jdbc.query[0].sql=select * from t_user where username=?
cas.authn.jdbc.query[0].fieldPassword=password
cas.authn.jdbc.query[0].driverClass=com.mysql.cj.jdbc.Driver
  • 前三行配置是數據庫基本鏈接配置,這個無需我多說。
  • 第四行表示配置用戶查詢 sql,根據用戶名查詢出用戶的全部信息。
  • 第五行表示數據庫中密碼的字段名字是什麼。
  • 第六行是數據庫驅動。

OK,配置完成後,接下來咱們就來重啓 CAS Server:

./build.sh run

啓動成功後,瀏覽器輸入 https://cas.javaboy.org:8443/cas/login 就能夠進入登陸頁面了(注意是 https 哦):

此時登陸用戶名就是 javaboy,密碼是 123。

2.3 CAS Client

接下來咱們再來看看 CAS Client 要作哪些完善。

接下來的配置在 Spring Boot 實現單點登陸的第三種方案! 一文的基礎上完成,因此還沒看前面文章的小夥伴建議先看一下哦。

同時,爲了案例簡潔,我這裏使用 JPA 來操做數據庫,要是你們不熟悉這塊的操做,能夠參考本系列以前的文章:Spring Security+Spring Data Jpa 強強聯手,安全管理只有更簡單!

CAS Client 中的對接主要是實現 UserDetailsService 接口。這裏要用到數據庫查詢,因此咱們首先添加數據庫相關依賴:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

而後在 application.properties 中配置數據庫鏈接信息:

spring.datasource.username=root
spring.datasource.password=123
spring.datasource.url=jdbc:mysql:///withjpa?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai

spring.jpa.database=mysql
spring.jpa.database-platform=mysql
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

都是常規配置,咱們就再也不重複解釋了。

接下來咱們建立兩個實體類,分別表示用戶角色了用戶類:

用戶角色:

@Entity(name = "t_role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String nameZh;
    //省略 getter/setter
}

這個實體類用來描述用戶角色信息,有角色 id、角色名稱(英文、中文),@Entity 表示這是一個實體類,項目啓動後,將會根據實體類的屬性在數據庫中自動建立一個角色表。

用戶實體類:

@Entity(name = "t_user")
public class User implements UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;
    private boolean accountNonExpired;
    private boolean accountNonLocked;
    private boolean credentialsNonExpired;
    private boolean enabled;
    @ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.PERSIST)
    private List<Role> roles;
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Role role : getRoles()) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return authorities;
    }
    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return accountNonExpired;
    }

    @Override
    public boolean isAccountNonLocked() {
        return accountNonLocked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return credentialsNonExpired;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }
    //省略其餘 get/set 方法
}

用戶實體類主要須要實現 UserDetails 接口,並實現接口中的方法。

這裏的字段基本都好理解,幾個特殊的我來稍微說一下:

  1. accountNonExpired、accountNonLocked、credentialsNonExpired、enabled 這四個屬性分別用來描述用戶的狀態,表示帳戶是否沒有過時、帳戶是否沒有被鎖定、密碼是否沒有過時、以及帳戶是否可用。
  2. roles 屬性表示用戶的角色,User 和 Role 是多對多關係,用一個 @ManyToMany 註解來描述。
  3. getAuthorities 方法返回用戶的角色信息,咱們在這個方法中把本身的 Role 稍微轉化一下便可。

數據模型準備好以後,咱們再來定義一個 UserDao:

public interface UserDao extends JpaRepository<User,Long> {
    User findUserByUsername(String username);
}

這裏的東西很簡單,咱們只須要繼承 JpaRepository 而後提供一個根據 username 查詢 user 的方法便可。若是小夥伴們不熟悉 Spring Data Jpa 的操做,能夠在公衆號後臺回覆 springboot 獲取鬆哥手敲的 Spring Boot 教程,裏邊有 jpa 相關操做,也能夠看看鬆哥錄製的視頻教程:Spring Boot + Vue 系列視頻教程

在接下來定義 UserService ,以下:

@Component
@Primary
public class UserDetailsServiceImpl implements UserDetailsService{

    @Autowired
    UserDao userDao;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userDao.findUserByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("用戶不存在");
        }
        return user;
    }
}

咱們本身定義的 UserDetailsServiceImpl 須要實現 UserDetailsService 接口,實現該接口,就要實現接口中的方法,也就是 loadUserByUsername。

OK ,如此以後,咱們的 CAS Client 如今就開發完成了,接下來啓動 CAS Client,啓動成功後,瀏覽器輸入 http://client1.cas.javaboy.org:8080/user/hello 訪問 hello 接口,此時會自動跳轉到 CAS Server 上登陸,登陸的用戶名密碼就是咱們存儲在數據庫中的用戶名密碼。登陸成功以後,通過兩個重定向,會從新回到 hello 接口。

hello 接口訪問成功以後,再去訪問 /user/hello 接口,就會發現權限配置也生效了。

這裏比較簡單,我就不給你們截圖了。

3.小結

好啦,今天主要和小夥伴們分享了一下 Spring Security + CAS 單點登陸中,如何使用本地數據庫。一個核心的思路是,認證由 CAS Server 來作,權限相關的操做,則仍是由 Spring Security 來完成。

好了 ,本文就說到這裏,本文相關案例我已經上傳到 GitHub ,你們能夠自行下載:https://github.com/lenve/spring-security-samples

好啦,小夥伴們若是以爲有收穫,記得點個在看鼓勵下鬆哥哦~

相關文章
相關標籤/搜索