一、SSO基本介绍
SSO(Single Sign-On,单点登录)是身份管理中的一部分。SSO的一种较为通俗的定义是:SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。它是目前比较流行的企业业务整合的解决方案之一。
相关技术主要有:spring security ,shiro
SSO的实际应用场景
SSO作为一种身份认证机制,在企业级应用中有着广泛的应用场景:
企业内部系统:企业内部通常有多个系统,如ERP、CRM、OA等,使用SSO可以让员工只需要登录一次就可以访问所有系统。
多应用集成:当企业需要集成多个第三方应用时,SSO可以提供统一的身份认证机制。
SaaS应用:SaaS提供商通常需要为多个客户提供服务,SSO可以让客户使用自己的企业账号登录。
移动应用:移动应用需要与后端系统进行交互,SSO可以提供统一的身份认证机制。
个人见解:SSO的技术价值
SSO的出现为企业级应用提供了一种统一的身份认证机制,其技术价值主要体现在:
提高用户体验:用户只需要登录一次就可以访问所有系统,减少了用户的登录次数和密码记忆负担。
提高安全性:SSO可以集中管理用户的身份认证,减少了密码泄露的风险。
简化系统集成:SSO可以简化系统之间的集成,减少了系统之间的认证交互。
降低运维成本:SSO可以集中管理用户的身份认证,降低了运维成本。
支持多因素认证:SSO可以支持多因素认证,提高了系统的安全性。
二、spring security 基本实现
1.引入pom依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
|
2.添加配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| @Configuration @EnableDiscoveryClient @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private Logger logger = LoggerFactory.getLogger(ActivitiConfig.class);
@Override @Autowired public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserDetailsService()); } @Bean public UserDetailsService myUserDetailsService() {
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
String[][] usersGroupsAndRoles = { {"salaboy", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam","GROUP_otherTeam"},
{"ryandawsonuk", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {"erdemedeiros", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"}, {"admin", "password", "ROLE_ACTIVITI_ADMIN"}, }; for (String[] user : usersGroupsAndRoles) { List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length)); logger.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]"); inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]), authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList()))); }
return inMemoryUserDetailsManager; }
@Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .anyRequest() .authenticated() .and() .httpBasic();
}
@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
}
|
启动测试:
三、spring security oauth2
1.认证服务
(1).引入pom依赖
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency>
|
(2).认证方式
授权码模式:authorization_code
客户端模式:client_credentials
简化模式: implicit
密码模式: password
(3).认证服务访问路径
/oauth/authorize:授权端点。
/oauth/token:令牌端点。
/oauth/confirm_access:用户确认授权提交端点。
/oauth/error:授权服务错误信息端点。
/oauth/check_token:用于资源服务访问的令牌解析端点。
/oauth/token_key:提供公有密匙的端点,如果你使用JWT令牌的话
(4).认证服务配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private TokenStore tokenStore; @Autowired private UserDetailsService userDetailsService; @Autowired private AuthenticationManager authenticationManager; @Autowired private BCryptPasswordEncoder passwordEncoder;
@Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.tokenKeyAccess("permitAll()"). checkTokenAccess("permitAll()"). allowFormAuthenticationForClients(); }
@Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory().withClient("password"). authorizedGrantTypes("authorization_code","client_credentials","implicit","password", "refresh_token"). accessTokenValiditySeconds(24*60*60*30*12). refreshTokenValiditySeconds(24*60*60*30*12*10). resourceIds("rid"). scopes("all"). secret(passwordEncoder.encode("123")) .autoApprove(false) .redirectUris("http://www.baidu.com"); }
@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(tokenStore()) .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); } @Bean public TokenStore tokenStore() { return new InMemoryTokenStore(); } }
|
(5).web安全配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| @Configuration @EnableWebSecurity @Slf4j public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserAuthenticationSuccessHandler userAuthenticationSuccessHandler;
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()); }
@Bean @Override protected AuthenticationManager authenticationManager() throws Exception { return super.authenticationManager(); }
@Bean @ConditionalOnMissingBean(UserDetailsService.class) @Override protected UserDetailsService userDetailsService() { InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager(); String[][] usersGroupsAndRoles = { {"salaboy", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam", "GROUP_otherTeam"},
{"ryandawsonuk", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {"erdemedeiros", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"}, {"admin", "password", "ROLE_ACTIVITI_ADMIN"}, }; for (String[] user : usersGroupsAndRoles) { List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length)); log.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]"); inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]), authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList()))); }
return inMemoryUserDetailsManager; }
@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll() .antMatchers("/oauth/**") .permitAll().and().httpBasic().and() .csrf().disable(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
}
|
(6).请求方式
授权码模式:
客户端获取code值
/oauth/authorize?client_id=password&response_type=code&scope=all&redirect_uri=http://www.baidu.com
client_id:客户端准入标识。
response_type:授权码模式固定为code。
scope:客户端权限。
redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数(授权码)
密码模式:
客户端模式:
简化模式:
浏览器访问:http://localhost:9006/oauth/authorize?client_id=password&response_type=token&scope=all&redirect_uri=http://www.baidu.com
2.资源服务
(1).引入pom依赖
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency>
|
(2).资源服务配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources .resourceId("rid"). .stateless(true); }
@Override public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() .antMatchers("/**/current/get","/**/oauth/**","/swagger-ui.html","/doc.html","/**/v2/api-docs/**","/error","/**/swagger-resources/**","/webjars/**") .permitAll() .antMatchers("/**") .hasAnyRole("ADMIN") .anyRequest() .authenticated(); }
}
|
四、oauth2集成redis
1.引入pom依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
|
2.yml 添加配置
1 2 3 4 5 6 7 8 9 10 11
| spring: redis: host: 192.168.42.146 port: 6379 database: 0 jedis: pool: max-active: 8 max-wait: -1 max-idle: 500 min-idle: 0
|
3.修改认证配置
1 2 3 4 5 6 7 8 9
| @Autowired private RedisConnectionFactory redisConnectionFactory; @Bean public TokenStore tokenStore() { return new RedisTokenStore(redisConnectionFactory); }
|
效果如下:
五、oauth2集成jwt
1.认证服务
(1).引入pom依赖
1 2 3 4
| <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-jose</artifactId> </dependency>
|
(2).生成密钥
keytool -genkey -alias youlai -keyalg RSA -keypass 000000 -keystore youlai.jks -storepass 00000
-genkey:生成密钥
-alias:别名
-keyalg:密钥算法
-keypass:密钥口令
-keystore:生成密钥库的存储路径和名称
-storepass:密钥库口令
将生成的youlai.jks添加到resource下
(3).修改配置文件
1 2 3 4 5 6
| encrypt: key-store: location: classpath:youlai.jks secret: 000000 alias: youlai password: 0000
|
(4).修改认证服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| @Autowired @Autowired private KeyProperties keyProperties; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain=new TokenEnhancerChain(); final List<TokenEnhancer> objects = Arrays.asList(jwtAccessTokenConverter(),consumerTokenEnhancer()); enhancerChain.setTokenEnhancers(objects); endpoints.tokenEnhancer(enhancerChain) .accessTokenConverter(jwtAccessTokenConverter()) .authenticationManager(authenticationManager) .tokenStore(tokenStore) .userDetailsService(userDetailsService) .reuseRefreshTokens(false); } public ConsumerTokenEnhancer consumerTokenEnhancer(){ return new ConsumerTokenEnhancer(expiraTime,redisConnectionFactory); } @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); }
@Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setKeyPair(keyPair()); return converter; }
@Bean public KeyPair keyPair(){ return (new KeyStoreKeyFactory(this.keyProperties.getKeyStore().getLocation(), this.keyProperties.getKeyStore().getSecret().toCharArray())).getKeyPair(this.keyProperties.getKeyStore().getAlias(), this.keyProperties.getKeyStore().getPassword().toCharArray());
}
|
2.资源服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
| @Autowired private TokenStore tokenStore; @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources .resourceId("rid").tokenStore(tokenStore) .stateless(true); } @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter(){ JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); Resource resource = new ClassPathResource("publicKey.txt"); String publicKey; try { publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream())); } catch (IOException e) { throw new RuntimeException(e); } converter.setVerifierKey(publicKey); return converter; }
## 六、OAuth2与JWT的技术价值
### OAuth2的技术价值
1. **安全的授权机制**:OAuth2提供了一种安全的授权机制,允许第三方应用在不获取用户密码的情况下访问用户资源。
2. **灵活的授权模式**:OAuth2支持多种授权模式,如授权码模式、密码模式、客户端模式等,适应不同的应用场景。
3. **细粒度的权限控制**:OAuth2可以通过scope参数实现细粒度的权限控制,限制第三方应用的访问范围。
4. **标准化**:OAuth2是一种标准化的协议,被广泛应用于各种应用场景。
### JWT的技术价值
1. **无状态**:JWT是无状态的,不需要在服务器端存储会话信息,减轻了服务器的负担。
2. **自包含**:JWT包含了所有必要的信息,如用户信息、权限等,减少了服务器的查询操作。
3. **安全**:JWT使用签名机制,确保了令牌的真实性和完整性。
4. **跨域支持**:JWT可以在不同的域之间传递,支持跨域认证。
5. **易于集成**:JWT易于集成到各种应用中,支持多种编程语言。
## 七、SSO性能优化
### 1. 令牌存储优化
- **使用Redis存储令牌**:将令牌存储在Redis中,提高令牌的读取和验证速度。 - **设置合理的令牌过期时间**:根据业务需求,设置合理的令牌过期时间,减少令牌的存储压力。 - **使用令牌黑名单**:对于已注销的令牌,使用黑名单机制,提高安全性。
### 2. 认证服务优化
- **使用缓存**:缓存用户信息和权限信息,减少数据库查询。 - **使用连接池**:使用数据库连接池,提高数据库访问速度。 - **优化认证流程**:减少认证过程中的网络往返次数,提高认证速度。
### 3. 资源服务优化
- **使用本地缓存**:缓存令牌验证结果,减少令牌验证的开销。 - **使用异步验证**:对于非关键操作,使用异步验证令牌,提高系统响应速度。 - **优化权限检查**:使用缓存和索引,提高权限检查的速度。
### 4. 网络优化
- **使用HTTPS**:使用HTTPS协议,提高通信的安全性。 - **压缩传输**:压缩令牌和其他数据,减少网络传输的开销。 - **使用CDN**:对于静态资源,使用CDN,提高资源的加载速度。
## 八、SSO的发展趋势
### 1. 技术演进
SSO作为一种身份认证机制,一直在不断演进:
- **从集中式到分布式**:SSO从集中式的认证服务发展到分布式的认证服务,提高了系统的可伸缩性和可靠性。
- **从基于Session到基于Token**:SSO从基于Session的认证发展到基于Token的认证,提高了系统的无状态性和可扩展性。
- **从单一认证到多因素认证**:SSO从单一的密码认证发展到多因素认证,提高了系统的安全性。
- **从企业内部到云端**:SSO从企业内部的认证服务发展到云端的认证服务,支持更多的应用场景。
### 2. 与新兴技术的融合
- **AI集成**:AI技术与SSO的结合,实现了智能身份认证和风险评估。
- **区块链**:区块链技术与SSO的结合,实现了去中心化的身份认证。
- **生物识别**:生物识别技术与SSO的结合,提高了身份认证的安全性和便捷性。
- **边缘计算**:边缘计算与SSO的结合,实现了边缘设备的身份认证。
### 3. 未来发展方向
- **标准化**:SSO将进一步标准化,提高不同系统之间的互操作性。
- **智能化**:SSO将更加智能化,实现智能身份认证和风险评估。
- **安全增强**:SSO将进一步加强安全性,如使用零知识证明、同态加密等技术。
- **用户体验**:SSO将进一步提高用户体验,如使用无密码认证、生物识别等技术。
## 九、总结与个人感悟
SSO作为一种身份认证机制,已经在企业级应用中得到了广泛的应用。它的出现为企业级应用提供了一种统一的身份认证机制,提高了用户体验和系统安全性。
### 技术价值
- **提高用户体验**:用户只需要登录一次就可以访问所有系统,减少了用户的登录次数和密码记忆负担。
- **提高安全性**:SSO可以集中管理用户的身份认证,减少了密码泄露的风险。
- **简化系统集成**:SSO可以简化系统之间的集成,减少了系统之间的认证交互。
- **降低运维成本**:SSO可以集中管理用户的身份认证,降低了运维成本。
### 应用价值
- **加速业务创新**:SSO的使用,加速了企业的业务创新和数字化转型。
- **提高业务效率**:SSO减少了用户的登录次数,提高了业务处理的效率。
- **增强市场竞争力**:通过提供更好的用户体验和更高的安全性,增强了企业的市场竞争力。
### 个人感悟
作为一名技术人员,我认为SSO的成功在于它的实用性和安全性。它不仅提供了一种统一的身份认证机制,还提高了系统的安全性和用户体验。
在未来的技术发展中,随着云计算、移动互联网和物联网的不断普及,SSO将会继续发挥重要的作用。对于我们来说,学习和掌握SSO不仅是了解一种技术,更是理解现代身份认证体系的重要途径。
SSO的设计理念和实现方式也值得我们学习,它展示了如何构建一个安全、可靠、易用的身份认证系统。这种学习不仅可以帮助我们更好地使用SSO,还可以启发我们在其他领域的设计和开发。
|