一,sso的用途 ?
1,如果有多個應用系統,用戶只需要登錄一次就可以訪問所有相互信任的應用系統。
不需要每次輸入用戶名稱和用戶密碼,
也不需要創建並記憶多套用戶名稱和用戶密碼。
2,系統管理員只需維護一套統一的用戶賬號,方便、簡單。
而不必管理很多套的用戶賬號。
3, 如果需要開發新的應用系統,可以直接使用單點登錄平台的用戶認證服務,簡化開發流程。
4,oauth和sso的區別:
oauth2解決的是服務提供方(微信等)給第三方應用授權的問題,
sso解決的是大型系統中各個子系統如何共享登陸狀態的問題
說明:劉宏締的架構森林是一個專注架構的博客,地址:https://www.cnblogs.com/architectforest
對應的源碼可以訪問這里獲取: https://github.com/liuhongdi/
說明:作者:劉宏締 郵箱: 371125307@qq.com
二,演示項目的相關信息
1,項目地址:
https://github.com/liuhongdi/securityssojwt
2,功能說明:
演示了基於oauth2實現sso
3,項目結構:如圖:
三,配置文件說明
1,ssoserver的 pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--security--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!--oauth2--> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> <version>2.5.0.RELEASE</version> </dependency> <!--jwt--> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> <version>1.1.1.RELEASE</version> </dependency> <!--jaxb--> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-core</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>javax.activation</groupId> <artifactId>activation</artifactId> <version>1.1.1</version> </dependency>
2,ssoclient1的 pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--security--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!--oauth2--> <dependency><groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> <version>2.5.0.RELEASE</version> </dependency> <!--jwt--> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> <version>1.1.1.RELEASE</version> </dependency> <!--oauth2 autoconfigure--> <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> <version>2.3.3.RELEASE</version> </dependency>
3,ssoclient2的 pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--security--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!--oauth2--> <dependency><groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> <version>2.5.0.RELEASE</version> </dependency> <!--jwt--> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> <version>1.1.1.RELEASE</version> </dependency> <!--oauth2 autoconfigure--> <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> <version>2.3.3.RELEASE</version> </dependency>
4,ssoserver的application.properties
server.port = 8080 server.servlet.context-path = /server spring.security.user.password=123456 #error server.error.include-stacktrace=always #log logging.level.org.springframework.web=trace logging.level.org.springframework.security=debug
5,ssoclient1的application.properties
security.oauth2.client.client-id=client1 security.oauth2.client.client-secret=client1secrect #需要認證時候跳轉的地址 security.oauth2.client.user-authorization-uri=http://127.0.0.1:8080/server/oauth/authorize #請求令牌地址 security.oauth2.client.access-token-uri=http://127.0.0.1:8080/server/oauth/token #解析 security.oauth2.resource.jwt.key-uri=http://127.0.0.1:8080/server/oauth/token_key #security.oauth2.resource.jwt.key-value=imooc #sso server.port=8081 server.servlet.context-path=/client1
6,ssoclient2的application.properties
security.oauth2.client.client-id=client2 security.oauth2.client.client-secret=client2secrect #需要認證時候跳轉的地址 security.oauth2.client.user-authorization-uri=http://127.0.0.1:8080/server/oauth/authorize #請求令牌地址 security.oauth2.client.access-token-uri=http://127.0.0.1:8080/server/oauth/token #解析 security.oauth2.resource.jwt.key-uri=http://127.0.0.1:8080/server/oauth/token_key #sso server.port=8082 server.servlet.context-path=/client2
四,java代碼說明:
1,ssoserver的WebSecurityConfig.java
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Resource private SsoUserDetailsService ssoUserDetailsService; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(ssoUserDetailsService).passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin().and().authorizeRequests().anyRequest().authenticated(); } }
2,ssoserver的SsoUserDetailsService.java:
@Component public class SsoUserDetailsService implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; //本來應該從數據庫加載數據,此處供僅演示 @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { System.out.println("----------------------loadUserByUsername"); if (username.equals("laoliu") == false) { throw new UsernameNotFoundException("用戶名不存在"); } return new User(username, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); } }
3,ssoserver的SsoAuthorizationServerConfig.java:
@Configuration @EnableAuthorizationServer public class SsoAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { //配置供訪問的客戶端的賬戶 @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() // 注冊一個客戶端,設置名稱 .withClient("client1") // 設置客戶端陰匙 .secret(new BCryptPasswordEncoder().encode("client1secrect")) // 對應客戶端登錄請求URI .redirectUris("http://127.0.0.1:8081/client1/login") // 授權方式 .authorizedGrantTypes("authorization_code", "password", "refresh_token") // 授權范圍 .scopes("all") // 是否自動同意,如果采用非自動同意,則需要用戶手動授權 .autoApprove(true) .and(). withClient("client2") .redirectUris("http://127.0.0.1:8082/client2/login") .secret(new BCryptPasswordEncoder().encode("client2secrect")) .authorizedGrantTypes("authorization_code", "password", "refresh_token") .scopes("all") .autoApprove(true); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter()); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.tokenKeyAccess("isAuthenticated()"); } @Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter(){ JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); //指定signkey converter.setSigningKey("liuhongdi"); return converter; } }
4,ssoclient1的HomeController.java
@Controller @RequestMapping("/home") public class HomeController { //查看登錄后的用戶信息 @RequestMapping("/session") @ResponseBody public String getsession(){ //session String userone = SessionUtil.getCurrentUserName(); System.out.println("user:"+userone); if (userone == null) { return "not login"; } else { return userone; } } }
5,ssoclient1的SessionUtil.java
public class SessionUtil { //得到security所保存的用戶 public static String getCurrentUserName(){ Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication.isAuthenticated() && !(authentication instanceof AnonymousAuthenticationToken)) { Object principal = authentication.getPrincipal(); //System.out.println(principal); if (principal instanceof String) { return (String)principal; } else if (principal instanceof UserDetails) { String currentuser = ((UserDetails) principal).getUsername(); return currentuser; } else { //System.out.println("not instanceof UserDetails"); } return null; } return null; } }
6,其他非關鍵代碼可訪問github查看
五,測試效果
1,按以下順序啟動三個模塊:
ssoserver
ssoclient1
ssoclient2
2,先訪問client1,
http://127.0.0.1:8081/client1/home/session
會跳轉到:
http://127.0.0.1:8080/server/login
如圖:
我們輸入用戶名 laoliu,密碼 123456
這個是寫死在代碼中的演示賬號
登錄后會跳轉到:
這個url會打印當前登錄用戶的用戶名
我們新打開一個標簽頁:
http://127.0.0.1:8082/client2/home/session
我們之前並未從client2登錄,查看效果:
可以看到也已經登錄
3,通過html頁面跳轉訪問:
http://127.0.0.1:8081/client1/index.html
返回:
點擊 訪問client2 的鏈接
可正常訪問
六,查看spring boot版本
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.3.RELEASE)