基於JWT實現SSO單點登入流程圖解
一、基於JWT實現SSO單點登入原理
1、什麼是單點登入
所謂單點登入就是有多個應用部署在不同的伺服器上,只需登入一次就可以互相訪問不同伺服器上的資源。
2、單點登入流程
當一個訪問請求發給應用A,如果這個請求需要登入以後才能訪問,那麼應用A就會向認證伺服器請求授權,這時候就把使用者引導到認證伺服器上。使用者在認證伺服器上完成認證並授權。認證授權完成後,認證伺服器返回給應用A一個授權碼,應用A攜帶授權碼到認證伺服器請求令牌,認證伺服器返回應用A一個JWT,應用A解析JWT裡面的資訊,完成登入。這是一個標準的OAuth2的授權碼流程。
走完認證流程後,給出去的JWT實際上裡面包含的就是當前使用者在認證伺服器上登入以後使用者的認證資訊,應用A解析JWT後,自己生成一個經過認證的Authentication放到它的SpringSecurity和SecurityContext裡面。
當訪問應用伺服器B的時候,同樣引導使用者去認證伺服器請求授權(不需要登入),使用者授權可以用登入的資訊去訪問應用B,後面同樣是授權碼流程,返回JWT給應用B。兩個應用返回不同的JWT,但是解析出的資訊是一樣的。
二、實現單點登入
1、父工程(sso-demo)
1)pom.xml
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.4.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> <version>2.1.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> <version>1.0.10.RELEASE</version> </dependency> </dependencies> </dependencyManagement>
2、認證服務(sso-server)
1)pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency>
2)application.properties
server.port = 9999
server.servlet.context-path = /server
3)WebSecurityConfig.java
@EnableWebSecurity @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ @Override protected void configure(HttpSecurity http) throws Exception { http.httpBasic().and().csrf().disable(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
4)MyUserDetailsService.java
@Component public class MyUserDetailsService implements UserDetailsService{ @Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { System.out.println("登入使用者名稱:"+username); String password = passwordEncoder.encode("123456"); return new User(username,password,true,AuthorityUtils.commaSeparatedStringToAuthorityList("all")); } }
5)SsoAuthorizationServerConfig.java
@Configuration @EnableAuthorizationServer public class SsoAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("appclient_1").secret(passwordEncoder.encode("client1_123456")) .authorizedGrantTypes("authorization_code","refresh_token") .scopes("all") .redirectUris("http://127.0.0.1:8080/client1/login") .and() .withClient("appclient_2").secret(passwordEncoder.encode("client2_123456")) .authorizedGrantTypes("authorization_code","refresh_token") .scopes("all") .redirectUris("http://127.0.0.1:8060/client2/login"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter()); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.tokenKeyAccess("isAuthenticated()");//訪問tokenKey(祕鑰shxiang)的時候需要身份認證 } @Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter(); accessTokenConverter.setSigningKey("shxiang");//設定祕鑰 return accessTokenConverter; } }
6)SsoServerApplication.java
@SpringBootApplication public class SsoServerApplication { public static void main(String[] args) { SpringApplication.run(SsoServerApplication.class,args); } }
3、應用1(sso-client1)
1)pom.xml,同上
2)application.properties
security.oauth2.client.client-id = appclient_1 security.oauth2.client.client-secret = client1_123456 security.oauth2.client.user-authorization-uri = http://127.0.0.1:9999/server/oauth/authorize security.oauth2.client.access-token-uri = http://127.0.0.1:9999/server/oauth/token security.oauth2.resource.jwt.key-uri = http://127.0.0.1:9999/server/oauth/token_key server.port=8080 server.servlet.context-path =/client1
3)index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>SSO Client1</title> </head> <body> <h1>SSO Demo Client1</h1> <a href="http://127.0.0.1:8060/client2/index.html" rel="external nofollow" >訪問Client2</a> </body> </html>
4)SsoClient1Application.java
@SpringBootApplication @RestController @EnableOAuth2Sso public class SsoClient1Application { public static void main(String[] args) { SpringApplication.run(SsoClient1Application.class,args); } @GetMapping("/user") public Authentication user(Authentication user) { return user; } }
4、應用2(sso-client2)
1)pom.xml,同上
2)application.properties,類比應用1修改
3)index.html,類比應用1修改
4)SsoClient2Application.java,同上
5、測試
1)瀏覽器輸入:127.0.0.1:8080/client1/index.html
2)使用者名稱隨便輸入,密碼輸入123456
3)點選Authorize
4)點選超級連結訪問Client2
5)點選Authorize
認證成功,後面點選兩個超級連結可以任意訪問,無需登入 、無需點選Authorize。
注意:
1)雖是同一使用者,但是訪問http://127.0.0.1:8080/client1/user和http://127.0.0.1:8060/client2/user獲取的Token值不一樣。
2)實現跳過授權,登入後直接訪問,修改如下程式碼:
3)表單登入與httpBasic登入,修改WebSecurityConfig.java中configure方法
httpBasic登入:http.httpBasic().and().csrf().disable();
表單登入:http.formLogin().and().authorizeRequests().anyRequest().authenticated();
4)重點:瀏覽器訪問要用127.0.0.1不要用localhost。要設定應用路徑server.servlet.context-path =/xxxx,不能直接到埠號。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。