- 在實現SSO之前,先要說一下**@EnableResourceServer與@EnableOAuth2Sso**
- 一般網關處我們使用EnableResourceServer時,可以支持任意的oauth2授權模型,網關聲明稱資源服務器可以配置ResourceServerConfigurerAdapter,我們可以配置放行的路徑等等內容。
- 如果網關處使用EnableOAuth2Sso,在所有請求轉發之前就首先要到認證服務器上去校驗信息,使用授權碼模式,clientid與clientsecret來確認進行oauth2的登陸,跳轉登陸頁面在進行登陸。
下面我們來簡單搭建一個SSO的小項目
-
搭建認證服務器
我們將客戶端信息存儲在mysql中,token使用jwt。
依賴包只需要oauth2,我cloud用的版本是Greenwich.SR2
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency>
-
配置AuthorizationServerConfigurerAdapter
package cn.hs.sys.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; import javax.sql.DataSource; /** * @description: * @Author: huangsan * @Date: 2020/5/8 4:46 下午 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Autowired private DataSource dataSource; @Autowired private BCryptPasswordEncoder passwordEncoder; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .tokenStore(tokenStore()) .tokenEnhancer(jwtAccessTokenConverter()) .authenticationManager(authenticationManager); } @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("123456"); return converter; } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { System.out.println("------"+passwordEncoder.encode("123456")); clients.jdbc(dataSource); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security .tokenKeyAccess("isAuthenticated()") .checkTokenAccess("isAuthenticated()"); } }
-
配置WebSecurityConfigurerAdapter
package cn.hs.sys.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; /** * @description: * @Author: huangsan * @Date: 2020/5/8 4:47 下午 */ @Configuration @EnableWebSecurity public class OAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Autowired private PasswordEncoder passwordEncoder; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService) .passwordEncoder(passwordEncoder); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
-
配置UserDetailsService(這裏自己實現邏輯返回UserDetails即可)
package cn.hs.sys.config; import cn.hs.sys.Constant; import cn.hs.sys.dao.SysUserEntity; import cn.hs.sys.dao.UserPermissionRepository; import org.apache.commons.lang.StringUtils; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; 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; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; /** * @description: * @Author: huangsan * @Date: 2020/5/8 4:53 下午 */ @Service public class UserDetailsServiceImpl implements UserDetailsService { @Resource private UserPermissionRepository userPermissionRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { if (StringUtils.isBlank(username)) { throw new UsernameNotFoundException("賬號錯誤"); } SysUserEntity sysUserEntity = userPermissionRepository.findUserIdByName(username); if (sysUserEntity == null) { throw new UsernameNotFoundException("賬號異常"); } if (Constant.USER_OFF.equals(sysUserEntity.getUserStatus())) { throw new UsernameNotFoundException("賬號狀態異常"); } if (sysUserEntity.getUserId() != null) { List<String> permissionStrs = userPermissionRepository.listPermissionById(sysUserEntity.getUserId()); List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); permissionStrs.forEach(permissionStr -> { if (StringUtils.isNotBlank(permissionStr)) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permissionStr); grantedAuthorities.add(grantedAuthority); } }); //密碼加密方式爲:passwordEncoder.encode() return new User(username, sysUserEntity.getUserPassword(), grantedAuthorities); } else { throw new UsernameNotFoundException("賬號異常"); } } }
-
配置文件如下(沒什麼東西,主要就是mysql的內容,eureka自行去掉吧)
server: port: 52188 spring: application: name: hs-auth datasource: url: jdbc:mysql://localhost:3306/hs_cloud?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver eureka: client: service-url: defaultZone: http://127.0.0.1:52199/eureka
-
認證服務器的內容基本都是一樣,沒什麼特別的地方我就直接貼代碼了,對於客戶端來說有一些需要注意的地方。
-
配置客戶端
注意客戶端不能使用EnableResourceServer註解
-
引入相關依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency>
-
在啓動類上增加@EnableOAuth2Sso註解
-
創建測試web測試類,返回數據
-
配置yml文件
server: port: 52109 servlet: context-path: /sys #特別需要配置,如果是/會反覆跳轉登陸頁面 security: oauth2: resource: jwt: key-uri: http://localhost:52188/oauth/token_key client: client-id: hs-system #客戶端id client-secret: 123456 #客戶端口令 access-token-uri: http://localhost:52188/oauth/token #獲取令牌路徑 user-authorization-uri: http://localhost:52188/oauth/authorize #獲取授權路徑
-
-
相同的方式配置另一個客戶端
和第一個客戶端一樣,注意更改path
-
配置完客戶端需要配置登陸跳轉路徑和自動授權(下面是sql插入語句自行修改)
INSERT INTO `hs_cloud`.`oauth_client_details`(`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('hs-gateway', NULL, '$2a$10$p/siIwq2lUsOpg6gZC1V5epmeLMdO6BrfKnO36.5ZCn9eHOcibiUi', 'read', 'authorization_code,refresh_token,password', 'http://localhost:52100/zuul/login', NULL, 3600, 360000, NULL, 'read'); INSERT INTO `hs_cloud`.`oauth_client_details`(`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('hs-modules-back', NULL, '$2a$10$p/siIwq2lUsOpg6gZC1V5epmeLMdO6BrfKnO36.5ZCn9eHOcibiUi', 'read', 'authorization_code,refresh_token,password', 'http://localhost:52110/back/login', NULL, 3600, 360000, NULL, 'read');
主要注意web_server_redirect_uri與autoapprove,配置autoapprove可以跳過授權的步驟,直接授權
-
成果演示(文字描述是:倆個客戶端訪問的時候都會去授權服務器,如果沒有登陸會跳轉登陸頁,當一個客戶端登錄後,可直接訪問,不需登陸。視頻傳上來好麻煩,就不傳了,手動狗頭。)