在前面的兩篇文章中,鬆哥和你們分享了 CAS Server 的搭建以及如何使用 Spring Security 搭建 CAS Client。java
可是前面的案例有一個問題,就是登陸用戶是在 CAS Server 配置文件中寫死的,沒有對接數據庫,實際項目中,這裏確定要對接數據庫,因此今天,鬆哥就來和你們聊一聊 CAS Server 如何對接數據庫。mysql
鬆哥最近和 Spring Security 槓上了,這是 Spring Security 系列的第 25 篇:git
先來看總體思路。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,而後咱們再去數據庫中根據用戶名獲取用戶的詳細信息,包括用戶的角色等,進而在後面的鑑權中用上角色。瀏覽器
好了,這是咱們一個大體的思路,接下來咱們來看具體實現。安全
接下來的配置在 鬆哥手把手教你入門 Spring Boot + CAS 單點登陸 一文的基礎上完成,因此還沒看前面文章的小夥伴建議先看一下哦。
首先咱們先在數據庫中準備一下用戶表、角色表以及用戶角色關聯表:
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);
而後咱們要在 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
OK,配置完成後,接下來咱們就來重啓 CAS Server:
./build.sh run
啓動成功後,瀏覽器輸入 https://cas.javaboy.org:8443/cas/login 就能夠進入登陸頁面了(注意是 https 哦):
此時登陸用戶名就是 javaboy,密碼是 123。
接下來咱們再來看看 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 接口,並實現接口中的方法。
這裏的字段基本都好理解,幾個特殊的我來稍微說一下:
數據模型準備好以後,咱們再來定義一個 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
接口,就會發現權限配置也生效了。
這裏比較簡單,我就不給你們截圖了。
好啦,今天主要和小夥伴們分享了一下 Spring Security + CAS 單點登陸中,如何使用本地數據庫。一個核心的思路是,認證由 CAS Server 來作,權限相關的操做,則仍是由 Spring Security 來完成。
好了 ,本文就說到這裏,本文相關案例我已經上傳到 GitHub ,你們能夠自行下載:https://github.com/lenve/spring-security-samples
好啦,小夥伴們若是以爲有收穫,記得點個在看鼓勵下鬆哥哦~