**
基于 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