基于 Spring Security OAuth2 SSO 单点登录系统
SSO简介
单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一退出(single sign-off)就是指,只需要单一的退出动作,就可以结束对于多个系统的访问权限。
Spring Security OAuth
Spring Security OAuth使用标准的Spring和Spring Security编程模型和配置惯例,为使用Spring Security with OAuth(1a)和OAuth2提供支持。OAuth协议
案例介绍
此工程分为三个模块:授权服务器(sso-auth-server)、web应用a(sso-client-a)、web应用b(sso-client-b),想达到的目的是:某一个用户在a系统登陆后在跳往b系统后不用在重复登录。
-
sso-auth-server:
- pom:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency> </dependencies>
- yml:
server: port: 8082 context-path: /auth_server
- SsoServerApplication.java
/** * @author Leone * @since 2018-05-07 **/ @SpringBootApplication public class SsoServerApplication { public static void main(String[] args) { SpringApplication.run(SsoServerApplication.class, args); } /** * 为测试环境添加相关的 Request Dumper information,便于调试 * * @return */ @Profile("!cloud") @Bean RequestDumperFilter requestDumperFilter() { return new RequestDumperFilter(); } }
- userDetailsService.java
/** * @author Leone * @since 2018-05-07 **/ @Component public class SsoUserDetailsService implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return new User(username, passwordEncoder.encode("admin"), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); } }
- SsoSecurityConfig.java
/** * @author Leone * @since 2018-05-07 **/ @Configuration public class SsoSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() .and().authorizeRequests() .antMatchers("/**/*.js", "/**/*.css", "/**/*.jpg", "/**/*.png") .permitAll() .anyRequest().authenticated() .and() .csrf().disable(); // http.formLogin().and().authorizeRequests().anyRequest().authenticated(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } }
- SsoAuthServerConfig.java
/** * @author Leone * @since 2018-05-07 **/ @Configuration @EnableAuthorizationServer public class SsoAuthServerConfig extends AuthorizationServerConfigurerAdapter { /** * 客户端一些配置 * * @param clients * @throws Exception */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("client1") .secret("secret1") .authorizedGrantTypes("authorization_code", "refresh_token") .scopes("all", "read", "write") .autoApprove(true) .and() .withClient("client2") .secret("secret2") .authorizedGrantTypes("authorization_code", "refresh_token") .scopes("all", "read", "write") .autoApprove(true); } /** * 配置jwtTokenStore * * @param endpoints * @throws Exception */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter()); } /** * springSecurity 授权表达式 * * @param security * @throws Exception */ @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.tokenKeyAccess("isAuthenticated()"); } /** * JwtTokenStore * * @return */ @Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } /** * 生成JTW token * * @return */ @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("andy"); return converter; } }
-
sso-client-a
- pom:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency> </dependencies>
- yml:
server: port: 8080 context-path: /clienta security: oauth2: client: clientId: client1 clientSecret: secret1 access-token-uri: http://127.0.0.1:8082/auth_server/oauth/token #请求令牌的地址 user-authorization-uri: http://127.0.0.1:8082/auth_server/oauth/authorize #请求认证的地址 resource: jwt: key-uri: http://127.0.0.1:8082/auth_server/oauth/token_key #解析jwt令牌所需要密钥的地址
- index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>sso-client-A</title> </head> <body> <h1>sso demo client-A</h1> <a href="http://127.0.0.1:8081/clientb/index.html">访问client-b</a> </body> </html>
- SsoClientA.java
/** * @author Leone * @since 2018-05-07 **/ @EnableOAuth2Sso @SpringBootApplication public class SsoClientA { public static void main(String[] args) { SpringApplication.run(SsoClientA.class, args); } }
-
sso-client-b
- pom:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency> </dependencies>
- yml:
server: port: 8081 context-path: /clientb security: oauth2: client: clientId: client2 clientSecret: secret2 access-token-uri: http://127.0.0.1:8082/auth_server/oauth/token user-authorization-uri: http://127.0.0.1:8082/auth_server/oauth/authorize resource: jwt: key-uri: http://127.0.0.1:8082/auth_server/oauth/token_key
- index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>sso-client-B</title> </head> <body> <h1>sso demo client-B</h1> <a href="http://127.0.0.1:8080/clienta/index.html">访问client-a</a> </body> </html>
- SsoClientA.java
/** * @author Leone * @since 2018-05-07 **/ @RestController @EnableOAuth2Sso @SpringBootApplication public class SsoClientB { @Autowired private OAuth2RestTemplate oAuth2RestTemplate; public static void main(String[] args) { SpringApplication.run(SsoClientB.class, args); } @GetMapping("/user") public Authentication user(Authentication user) { return user; } @Bean public OAuth2RestTemplate oAuth2RestTemplate(OAuth2ClientContext oAuth2ClientContext, OAuth2ProtectedResourceDetails details){ return new OAuth2RestTemplate(details,oAuth2ClientContext); } }
项目源码:git@github.com:janlle/sso-server.git