diff --git a/pom.xml b/pom.xml index 532d379df..7b4589f5f 100644 --- a/pom.xml +++ b/pom.xml @@ -69,6 +69,9 @@ spring-boot-demo-elasticsearch-rest-high-level-client spring-boot-demo-https spring-boot-demo-flyway + spring-boot-demo-sso-server + spring-boot-demo-sso-client1 + spring-boot-demo-sso-client2 pom @@ -134,6 +137,13 @@ UserAgentUtils ${user.agent.version} + + org.springframework.cloud + spring-cloud-dependencies + Greenwich.SR1 + pom + import + diff --git a/spring-boot-demo-https/pom.xml b/spring-boot-demo-https/pom.xml index bfd1b354a..a5b072ccd 100644 --- a/spring-boot-demo-https/pom.xml +++ b/spring-boot-demo-https/pom.xml @@ -33,6 +33,7 @@ + spring-boot-demo-https org.springframework.boot diff --git a/spring-boot-demo-https/src/test/java/com/xkcoding/springbootdemohttps/SpringBootDemoHttpsApplicationTests.java b/spring-boot-demo-https/src/test/java/com/xkcoding/springbootdemohttps/SpringBootDemoHttpsApplicationTests.java new file mode 100644 index 000000000..ce62f8353 --- /dev/null +++ b/spring-boot-demo-https/src/test/java/com/xkcoding/springbootdemohttps/SpringBootDemoHttpsApplicationTests.java @@ -0,0 +1,13 @@ +package com.xkcoding.springbootdemohttps; + +import org.junit.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SpringBootDemoHttpsApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/spring-boot-demo-sso-client1/README.md b/spring-boot-demo-sso-client1/README.md new file mode 100644 index 000000000..df57fc726 --- /dev/null +++ b/spring-boot-demo-sso-client1/README.md @@ -0,0 +1,6 @@ + + +## sso 单点登录系统 客户端 + + +全流程详见 服务端`spring-boot-demo-sso-server` diff --git a/spring-boot-demo-sso-client1/pom.xml b/spring-boot-demo-sso-client1/pom.xml new file mode 100644 index 000000000..db0d57869 --- /dev/null +++ b/spring-boot-demo-sso-client1/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + + com.xkcoding + spring-boot-demo + 1.0.0-SNAPSHOT + + + spring-boot-demo-sso-client1 + spring-boot-demo-sso-client1 + 0.0.1-SNAPSHOT + spring-boot-demo-sso-client1 + Demo project for Spring Boot + + + 1.8 + + + + + org.springframework.cloud + spring-cloud-starter-oauth2 + 2.0.1.RELEASE + + + org.springframework.boot + spring-boot-starter-web + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-boot-demo-sso-client1/src/main/java/com/xcoding/sso/SpringBootDemoSsoClient1Application.java b/spring-boot-demo-sso-client1/src/main/java/com/xcoding/sso/SpringBootDemoSsoClient1Application.java new file mode 100644 index 000000000..2b46ffbc2 --- /dev/null +++ b/spring-boot-demo-sso-client1/src/main/java/com/xcoding/sso/SpringBootDemoSsoClient1Application.java @@ -0,0 +1,18 @@ +package com.xcoding.sso; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + + +/** + * @author Administrator + */ +@SpringBootApplication +public class SpringBootDemoSsoClient1Application { + + public static void main(String[] args) { + SpringApplication.run(SpringBootDemoSsoClient1Application.class, args); + } + + +} diff --git a/spring-boot-demo-sso-client1/src/main/java/com/xcoding/sso/config/ClientWebSecurityConfigurer.java b/spring-boot-demo-sso-client1/src/main/java/com/xcoding/sso/config/ClientWebSecurityConfigurer.java new file mode 100644 index 000000000..84cafc5ae --- /dev/null +++ b/spring-boot-demo-sso-client1/src/main/java/com/xcoding/sso/config/ClientWebSecurityConfigurer.java @@ -0,0 +1,31 @@ +package com.xcoding.sso.config; + +import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +/** + * @author chen.chao + * @version 1.0 + * @date 2020/4/16 9:41 + * @description + */ + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +@EnableOAuth2Sso +public class ClientWebSecurityConfigurer extends WebSecurityConfigurerAdapter { + + @Override + public void configure(HttpSecurity http) throws Exception { + // 需要先于需授权地址 + http.antMatcher("/**").authorizeRequests().antMatchers("/free").permitAll() + .anyRequest().authenticated(); +/* http.antMatcher("/**").authorizeRequests() + .anyRequest().authenticated().antMatchers("/free").permitAll();*/ + } +} diff --git a/spring-boot-demo-sso-client1/src/main/java/com/xcoding/sso/controller/IndexController.java b/spring-boot-demo-sso-client1/src/main/java/com/xcoding/sso/controller/IndexController.java new file mode 100644 index 000000000..663119b9d --- /dev/null +++ b/spring-boot-demo-sso-client1/src/main/java/com/xcoding/sso/controller/IndexController.java @@ -0,0 +1,33 @@ +package com.xcoding.sso.controller; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author chen.chao + * @version 1.0 + * @date 2020/4/14 17:25 + * @description + */ +@RestController +public class IndexController { + + + @GetMapping("/free") + public String normal( ) { + return "不需要授权路径!"; + } + + @GetMapping("/user") + @PreAuthorize("hasAuthority('ROLE_USER')") + public String medium() { + return "用户权限路径"; + } + + @GetMapping("/admin") + @PreAuthorize("hasAuthority('ROLE_ADMIN')") + public String admin() { + return "管理员权限路径"; + } +} diff --git a/spring-boot-demo-sso-client1/src/main/resources/application.yml b/spring-boot-demo-sso-client1/src/main/resources/application.yml new file mode 100644 index 000000000..a408a8d19 --- /dev/null +++ b/spring-boot-demo-sso-client1/src/main/resources/application.yml @@ -0,0 +1,23 @@ + + +# sso-server地址 +auth-server: http://localhost:9900/uac + +server: + port: 9901 +security: + oauth2: + client: + client-id: client1 + client-secret: client1 + #请求认证的地址 + user-authorization-uri: ${auth-server}/oauth/authorize + #请求令牌的地址 + access-token-uri: ${auth-server}/oauth/token + resource: + jwt: + #解析jwt令牌所需要密钥的地址 + key-uri: ${auth-server}/oauth/token_key + +debug: true + diff --git a/spring-boot-demo-sso-client2/README.md b/spring-boot-demo-sso-client2/README.md new file mode 100644 index 000000000..df57fc726 --- /dev/null +++ b/spring-boot-demo-sso-client2/README.md @@ -0,0 +1,6 @@ + + +## sso 单点登录系统 客户端 + + +全流程详见 服务端`spring-boot-demo-sso-server` diff --git a/spring-boot-demo-sso-client2/pom.xml b/spring-boot-demo-sso-client2/pom.xml new file mode 100644 index 000000000..7efeb9fd0 --- /dev/null +++ b/spring-boot-demo-sso-client2/pom.xml @@ -0,0 +1,34 @@ + + + + spring-boot-demo + com.xkcoding + 1.0.0-SNAPSHOT + + 4.0.0 + + spring-boot-demo-client2 + + + + org.springframework.cloud + spring-cloud-starter-oauth2 + 2.0.1.RELEASE + + + org.springframework.boot + spring-boot-starter-web + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-demo-sso-client2/src/main/java/com/xcoding/sso/SpringBootDemoSsoClient2Application.java b/spring-boot-demo-sso-client2/src/main/java/com/xcoding/sso/SpringBootDemoSsoClient2Application.java new file mode 100644 index 000000000..8265bc283 --- /dev/null +++ b/spring-boot-demo-sso-client2/src/main/java/com/xcoding/sso/SpringBootDemoSsoClient2Application.java @@ -0,0 +1,18 @@ +package com.xcoding.sso; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + + +/** + * @author Administrator + */ +@SpringBootApplication +public class SpringBootDemoSsoClient2Application { + + public static void main(String[] args) { + SpringApplication.run(SpringBootDemoSsoClient2Application.class, args); + } + + +} diff --git a/spring-boot-demo-sso-client2/src/main/java/com/xcoding/sso/config/ClientWebSecurityConfigurer.java b/spring-boot-demo-sso-client2/src/main/java/com/xcoding/sso/config/ClientWebSecurityConfigurer.java new file mode 100644 index 000000000..19df996f5 --- /dev/null +++ b/spring-boot-demo-sso-client2/src/main/java/com/xcoding/sso/config/ClientWebSecurityConfigurer.java @@ -0,0 +1,28 @@ +package com.xcoding.sso.config; + +import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +/** + * @author chen.chao + * @version 1.0 + * @date 2020/4/16 9:41 + * @description + */ + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +@EnableOAuth2Sso +public class ClientWebSecurityConfigurer extends WebSecurityConfigurerAdapter { + + @Override + public void configure(HttpSecurity http) throws Exception { + http.antMatcher("/**").authorizeRequests().antMatchers("/free").permitAll() + .anyRequest().authenticated(); + } +} diff --git a/spring-boot-demo-sso-client2/src/main/java/com/xcoding/sso/controller/IndexController.java b/spring-boot-demo-sso-client2/src/main/java/com/xcoding/sso/controller/IndexController.java new file mode 100644 index 000000000..663119b9d --- /dev/null +++ b/spring-boot-demo-sso-client2/src/main/java/com/xcoding/sso/controller/IndexController.java @@ -0,0 +1,33 @@ +package com.xcoding.sso.controller; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author chen.chao + * @version 1.0 + * @date 2020/4/14 17:25 + * @description + */ +@RestController +public class IndexController { + + + @GetMapping("/free") + public String normal( ) { + return "不需要授权路径!"; + } + + @GetMapping("/user") + @PreAuthorize("hasAuthority('ROLE_USER')") + public String medium() { + return "用户权限路径"; + } + + @GetMapping("/admin") + @PreAuthorize("hasAuthority('ROLE_ADMIN')") + public String admin() { + return "管理员权限路径"; + } +} diff --git a/spring-boot-demo-sso-client2/src/main/resources/application.yml b/spring-boot-demo-sso-client2/src/main/resources/application.yml new file mode 100644 index 000000000..27d2a38ae --- /dev/null +++ b/spring-boot-demo-sso-client2/src/main/resources/application.yml @@ -0,0 +1,23 @@ + + +# sso-server地址 +auth-server: http://localhost:9900/uac + +server: + port: 9902 +security: + oauth2: + client: + client-id: client2 + client-secret: client2 + #请求认证的地址 + user-authorization-uri: ${auth-server}/oauth/authorize + #请求令牌的地址 + access-token-uri: ${auth-server}/oauth/token + resource: + jwt: + #解析jwt令牌所需要密钥的地址 + key-uri: ${auth-server}/oauth/token_key + +debug: true + diff --git a/spring-boot-demo-sso-server/README.md b/spring-boot-demo-sso-server/README.md new file mode 100644 index 000000000..c6e3b3944 --- /dev/null +++ b/spring-boot-demo-sso-server/README.md @@ -0,0 +1,419 @@ + + + +## 单点登录系统示例(sso) + +> 使用 `oauth2` `spring security` 一个授权服务器 + 两个 客户端 + +流程: +1. 客户端1 , 客户端2 不同权限路径 + - `/free` 无需授权 + - `/user` 用户权限 + - `/admin` 管理员权限 +2. 访问客户端1`/free` 正常访问 +3. 访问客户端1`/user` 获取授权信息 + 1. 未登录跳转授权服务器登录 + 2. 登录有权限 正常访问 , 无权限 拒绝 +4. 访问客户端2`/user` 如有权限自动登录 +5. `/admin` 同理 , 客户端1 客户端2 访问顺序可随意调换 , 实现 一处登录处处访问 + + +### 服务端 + +服务器端关键pom如下 + +```xml + + + org.springframework.cloud + spring-cloud-starter-oauth2 + 2.0.1.RELEASE + + + org.springframework.boot + spring-boot-starter-web + + +``` + +**注意** 不同版本spring boot 对 `spring-cloud-starter-oauth2` 有不兼容情况 可以使用`spring-cloud-dependencies` 管理 + + +1. 配置授权服务器 客户端 + + +```java + +package com.xkcoding.sso.config; + +import jdk.internal.dynalink.support.NameCodec; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.config.annotation.builders.ClientDetailsServiceBuilder; +import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder; +import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; +import org.springframework.security.oauth2.provider.token.DefaultTokenServices; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; +import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * @author chen.chao + * @version 1.0 + * @date 2020/4/13 17:50 + * @description 配置授权认证 + */ + +@Configuration +@EnableAuthorizationServer +@EnableConfigurationProperties(SsoClientProperties.class) +public class SsoAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { + + // 引入 配置文件 也可以写死 正常应该是写在数据库中 + @Autowired + private SsoClientProperties ssoClientProperties; + + /** + * 客户端一些配置 + * + * @param clients + * @throws Exception + */ + @Override + public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + // 从配置文件中读取 客户端授权信息 相当于把要接入单点系统的客户端id和密钥等信息存进来并设定权限 + ClientDetailsServiceBuilder memory = clients.inMemory(); + List client = ssoClientProperties.getClient(); + for (SsoClientProperties.SsoClient c : client) { + try { + memory = memory.withClient(c.getClientName()) + .secret(passwordEncoder.encode(c.getClientPassword())) + .authorizedGrantTypes("authorization_code", "refresh_token") + .scopes("all") + .redirectUris(c.getUri()) + .autoApprove(false).and(); + } catch (Exception e) { + e.printStackTrace(); + } + } + // 也可以使用数据库动态配置 , 如userDetail 一样 + // clients.withClientDetails() + } + + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { + endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter()); + DefaultTokenServices tokenServices = (DefaultTokenServices) endpoints.getDefaultAuthorizationServerTokenServices(); + tokenServices.setTokenStore(endpoints.getTokenStore()); + tokenServices.setSupportRefreshToken(true); + tokenServices.setClientDetailsService(endpoints.getClientDetailsService()); + tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer()); + tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.MINUTES.toSeconds(1)); + endpoints.tokenServices(tokenServices); + } + + @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(); + converter.setSigningKey("setSigningKey"); + return converter; + } + +} + + + +``` + + +2. 然后配置一下spring security + +```java + +package com.xkcoding.sso.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + + +/** + * @author chen.chao + * @version 1.0 + * @date 2020/4/13 17:50 + * @description 配置 spring security + */ +@Configuration +public class SsoSecurityConfig extends WebSecurityConfigurerAdapter { + + + @Autowired + private UserDetailsService userDetailsService; + + + @Override + @Bean + public AuthenticationManager authenticationManager() throws Exception { + return super.authenticationManager(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public DaoAuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); + authenticationProvider.setUserDetailsService(userDetailsService); + authenticationProvider.setPasswordEncoder(passwordEncoder()); + authenticationProvider.setHideUserNotFoundExceptions(false); + return authenticationProvider; + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + + http + .requestMatchers().antMatchers("/oauth/**", "/login/**", "/logout/**") + .and() + .authorizeRequests() + .antMatchers("/oauth/**").authenticated() + .and() + .formLogin().permitAll(); + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.authenticationProvider(authenticationProvider()); + } +} + + +``` + + +3. 配置`userDetail` + +```java + +package com.xkcoding.sso.service; + +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author chen.chao + * @version 1.0 + * @date 2020/4/13 17:50 + * @description + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + + + private static Map USER_DB = new HashMap<>(); + + + static { + PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + USER_DB.put("user", new User("user", passwordEncoder.encode("111"), + AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"))); + USER_DB.put("admin", new User("admin", passwordEncoder.encode("222"), + AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN"))); + USER_DB.put("super", new User("super", passwordEncoder.encode("333"), + AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN,ROLE_USER"))); + + } + + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + // 从数据库中查询 + User user = USER_DB.get(username); + if (user == null) { + throw new UsernameNotFoundException(String.format("用户%s不存在", username)); + } + return user; + } + + +} + + +``` + +这样 授权服务器就配置完成 + +### 客户端1和客户端2 + +> 客户端1,2 相同 只是换了个端口 只讲一遍 + + +1. 首先依赖 + +```xml + + + org.springframework.cloud + spring-cloud-starter-oauth2 + 2.0.1.RELEASE + + + org.springframework.boot + spring-boot-starter-web + + +``` + +2.yml 中自动配置项 +```yaml + + + +# sso-server地址 +auth-server: http://localhost:9900/uac + +# 这个客户端的端口 +server: + port: 9902 + + +security: + oauth2: + client: +#客户端id +#客户端密码 +# 这两项务必要与授权服务器相同 + client-id: client2 + client-secret: client2 + #请求认证的地址 + user-authorization-uri: ${auth-server}/oauth/authorize + #请求令牌的地址 + access-token-uri: ${auth-server}/oauth/token + resource: + jwt: + #解析jwt令牌所需要密钥的地址 + key-uri: ${auth-server}/oauth/token_key + +``` + + +3. 客户端配置 + +```java + +package com.xcoding.sso.config; + +import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +/** + * @author chen.chao + * @version 1.0 + * @date 2020/4/16 9:41 + * @description + */ + +@Configuration +@EnableOAuth2Sso +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class ClientWebSecurityConfigurer extends WebSecurityConfigurerAdapter { + + @Override + public void configure(HttpSecurity http) throws Exception { + // 这里把/free 放行 + http.antMatcher("/**").authorizeRequests().antMatchers("/free").permitAll() + .anyRequest().authenticated(); + } +} + +``` + +3. 写几个访问方法测试路径 + +```java + +package com.xcoding.sso.controller; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author chen.chao + * @version 1.0 + * @date 2020/4/14 17:25 + * @description + */ +@RestController +public class IndexController { + + + @GetMapping("/free") + public String normal( ) { + return "不需要授权路径!"; + } + + @GetMapping("/user") + @PreAuthorize("hasAuthority('ROLE_USER')") + public String medium() { + return "用户权限路径"; + } + + @GetMapping("/admin") + @PreAuthorize("hasAuthority('ROLE_ADMIN')") + public String admin() { + return "管理员权限路径"; + } +} + + +``` + +最后启动,先启动授权服务器,然后启动客户端 + diff --git a/spring-boot-demo-sso-server/pom.xml b/spring-boot-demo-sso-server/pom.xml new file mode 100644 index 000000000..c761081d1 --- /dev/null +++ b/spring-boot-demo-sso-server/pom.xml @@ -0,0 +1,65 @@ + + + 4.0.0 + + + com.xkcoding + spring-boot-demo + 1.0.0-SNAPSHOT + + + spring-boot-demo-sso + spring-boot-demo-sso-server + 0.0.1-SNAPSHOT + spring-boot-demo-sso-server + Demo project for Spring Boot + + + 1.8 + + + + + org.springframework.cloud + spring-cloud-starter-oauth2 + 2.0.1.RELEASE + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/SpringBootDemoSsoServerApplication.java b/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/SpringBootDemoSsoServerApplication.java new file mode 100644 index 000000000..bee3cabd7 --- /dev/null +++ b/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/SpringBootDemoSsoServerApplication.java @@ -0,0 +1,17 @@ +package com.xkcoding.sso; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * @author chen.chao + */ +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class SpringBootDemoSsoServerApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootDemoSsoServerApplication.class, args); + } + +} diff --git a/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/config/SsoAuthorizationServerConfig.java b/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/config/SsoAuthorizationServerConfig.java new file mode 100644 index 000000000..4f50b0d07 --- /dev/null +++ b/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/config/SsoAuthorizationServerConfig.java @@ -0,0 +1,97 @@ +package com.xkcoding.sso.config; + +import jdk.internal.dynalink.support.NameCodec; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.config.annotation.builders.ClientDetailsServiceBuilder; +import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder; +import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; +import org.springframework.security.oauth2.provider.token.DefaultTokenServices; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; +import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * @author chen.chao + * @version 1.0 + * @date 2020/4/13 17:50 + * @description 配置授权认证 + */ + +@Configuration +@EnableAuthorizationServer +@EnableConfigurationProperties(SsoClientProperties.class) +public class SsoAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { + + @Autowired + private SsoClientProperties ssoClientProperties; + + /** + * 客户端一些配置 + * + * @param clients + * @throws Exception + */ + @Override + public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + // 从配置文件中读取 客户端授权信息 相当于把要接入单点系统的客户端id和密钥等信息存进来并设定权限 + ClientDetailsServiceBuilder memory = clients.inMemory(); + List client = ssoClientProperties.getClient(); + for (SsoClientProperties.SsoClient c : client) { + try { + memory = memory.withClient(c.getClientName()) + .secret(passwordEncoder.encode(c.getClientPassword())) + .authorizedGrantTypes("authorization_code", "refresh_token") + .scopes("all") + .redirectUris(c.getUri()) + .autoApprove(false).and(); + } catch (Exception e) { + e.printStackTrace(); + } + } + // 也可以使用数据库动态配置 + // clients.withClientDetails() + } + + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { + endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter()); + DefaultTokenServices tokenServices = (DefaultTokenServices) endpoints.getDefaultAuthorizationServerTokenServices(); + tokenServices.setTokenStore(endpoints.getTokenStore()); + tokenServices.setSupportRefreshToken(true); + tokenServices.setClientDetailsService(endpoints.getClientDetailsService()); + tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer()); + tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.MINUTES.toSeconds(1)); + endpoints.tokenServices(tokenServices); + } + + @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(); + converter.setSigningKey("setSigningKey"); + return converter; + } + +} diff --git a/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/config/SsoClientProperties.java b/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/config/SsoClientProperties.java new file mode 100644 index 000000000..510308945 --- /dev/null +++ b/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/config/SsoClientProperties.java @@ -0,0 +1,33 @@ +package com.xkcoding.sso.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author chen.chao + * @version 1.0 + * @date 2020/4/14 17:47 + * @description + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "sso") +public class SsoClientProperties { + + private List client; + + @Data + static class SsoClient{ + + private String clientName; + + private String clientPassword; + + private String uri; + + } +} diff --git a/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/config/SsoSecurityConfig.java b/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/config/SsoSecurityConfig.java new file mode 100644 index 000000000..52145dac7 --- /dev/null +++ b/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/config/SsoSecurityConfig.java @@ -0,0 +1,66 @@ +package com.xkcoding.sso.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + + +/** + * @author chen.chao + * @version 1.0 + * @date 2020/4/13 17:50 + * @description 配置 spring security + */ +@Configuration +public class SsoSecurityConfig extends WebSecurityConfigurerAdapter { + + + @Autowired + private UserDetailsService userDetailsService; + + + @Override + @Bean + public AuthenticationManager authenticationManager() throws Exception { + return super.authenticationManager(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public DaoAuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); + authenticationProvider.setUserDetailsService(userDetailsService); + authenticationProvider.setPasswordEncoder(passwordEncoder()); + authenticationProvider.setHideUserNotFoundExceptions(false); + return authenticationProvider; + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + + http + .requestMatchers().antMatchers("/oauth/**", "/login/**", "/logout/**") + .and() + .authorizeRequests() + .antMatchers("/oauth/**").authenticated() + .and() + .formLogin().permitAll(); + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.authenticationProvider(authenticationProvider()); + } +} diff --git a/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/service/UserDetailsServiceImpl.java b/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/service/UserDetailsServiceImpl.java new file mode 100644 index 000000000..84433725e --- /dev/null +++ b/spring-boot-demo-sso-server/src/main/java/com/xkcoding/sso/service/UserDetailsServiceImpl.java @@ -0,0 +1,51 @@ +package com.xkcoding.sso.service; + +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author chen.chao + * @version 1.0 + * @date 2020/4/13 17:50 + * @description + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + + + private static Map USER_DB = new HashMap<>(); + + + static { + PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + USER_DB.put("user", new User("user", passwordEncoder.encode("111"), + AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"))); + USER_DB.put("admin", new User("admin", passwordEncoder.encode("222"), + AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN"))); + USER_DB.put("super", new User("super", passwordEncoder.encode("333"), + AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN,ROLE_USER"))); + + } + + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + // 从数据库中查询 + User user = USER_DB.get(username); + if (user == null) { + throw new UsernameNotFoundException(String.format("用户%s不存在", username)); + } + return user; + } + + +} diff --git a/spring-boot-demo-sso-server/src/main/resources/application.yml b/spring-boot-demo-sso-server/src/main/resources/application.yml new file mode 100644 index 000000000..d6414e461 --- /dev/null +++ b/spring-boot-demo-sso-server/src/main/resources/application.yml @@ -0,0 +1,17 @@ + +server: + port: 9900 + servlet: + context-path: /uac + + + +sso.client: + # 以下信息需要与客户端对应 同时uri 版本默认是附带login,有些版本不填将出现无效请求 有些版本则可以不填uri + - client-name: client1 + client-password: client1 + uri: http://localhost:9901/login + - client-name: client2 + client-password: client2 + uri: http://localhost:9902/login + diff --git a/spring-boot-demo-sso-server/src/test/java/springbootdemosso/sso/SsoApplicationTests.java b/spring-boot-demo-sso-server/src/test/java/springbootdemosso/sso/SsoApplicationTests.java new file mode 100644 index 000000000..6d60220fb --- /dev/null +++ b/spring-boot-demo-sso-server/src/test/java/springbootdemosso/sso/SsoApplicationTests.java @@ -0,0 +1,13 @@ +package springbootdemosso.sso; + +import org.junit.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SsoApplicationTests { + + @Test + void contextLoads() { + } + +}