├── README.md
├── docker-compose.yml
├── pom.xml
├── sso-client1
├── Dockerfile
├── build.sh
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── cn
│ │ └── merryyou
│ │ └── sso
│ │ └── client
│ │ └── SsoClient1Application.java
│ └── resources
│ ├── application.yml
│ └── static
│ └── index.html
├── sso-client2
├── Dockerfile
├── build.sh
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── cn
│ │ └── merryyou
│ │ └── soo
│ │ └── client
│ │ └── SsoClient2Application.java
│ └── resources
│ ├── application.yml
│ └── static
│ └── index.html
├── sso-resource
├── Dockerfile
├── build.sh
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── cn
│ │ └── merryyou
│ │ └── sso
│ │ ├── SsoResourceApplication.java
│ │ └── resource
│ │ └── SsoResourceServerConfig.java
│ └── resources
│ └── application.yml
└── sso-server
├── Dockerfile
├── build.sh
├── pom.xml
└── src
└── main
├── java
└── cn
│ └── merryyou
│ ├── SsoServerApplication.java
│ └── sso
│ ├── controller
│ └── LoginController.java
│ └── server
│ ├── SsoAuthorizationServerConfig.java
│ ├── SsoSecurityConfig.java
│ └── SsoUserDetailsService.java
└── resources
├── application.yml
├── static
├── css
│ ├── common.css
│ ├── font-awesome.min.css
│ ├── reset.css
│ └── style.css
├── fonts
│ └── fontawesome-webfont.woff2
├── images
│ ├── banner.png
│ ├── cut.jpg
│ ├── logo_bg.jpg
│ └── logowz.png
└── js
│ ├── common.js
│ └── jquery.min.js
└── templates
└── ftl
└── login.ftl
/README.md:
--------------------------------------------------------------------------------
1 | > [单点登录](https://zh.wikipedia.org/wiki/%E5%96%AE%E4%B8%80%E7%99%BB%E5%85%A5)(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指,只需要单一的注销动作,就可以结束对于多个系统的访问权限。
2 |
3 | ## Security OAuth2 单点登录流程示意图
4 |
5 | [](https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/SpringSecurity-OAuth2-sso.png "https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/SpringSecurity-OAuth2-sso.png")
6 |
7 |
8 | 1. 访问client1
9 | 2. `client1`将请求导向`sso-server`
10 | 3. 同意授权
11 | 4. 携带授权码`code`返回`client1`
12 | 5. `client1`拿着授权码请求令牌
13 | 6. 返回`JWT`令牌
14 | 7. `client1`解析令牌并登录
15 | 8. `client1`访问`client2`
16 | 9. `client2`将请求导向`sso-server`
17 | 10. 同意授权
18 | 11. 携带授权码`code`返回`client2`
19 | 12. `client2`拿着授权码请求令牌
20 | 13. 返回`JWT`令牌
21 | 14. `client2`解析令牌并登录
22 |
23 | 用户的登录状态是由`sso-server`认证中心来保存的,登录界面和账号密码的验证也是`sso-server`认证中心来做的(**`client1`和`clien2`返回`token`是不同的,但解析出来的用户信息是同一个用户**)。
24 |
25 | ## Security OAuth2 实现单点登录
26 |
27 | ### 项目结构
28 | [](https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth2-sso01.png "https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth2-sso01.png")
29 |
30 | ### sso-server
31 | #### 认证服务器
32 | ```java
33 | @Configuration
34 | @EnableAuthorizationServer
35 | public class SsoAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
36 |
37 | /**
38 | * 客户端一些配置
39 | * @param clients
40 | * @throws Exception
41 | */
42 | @Override
43 | public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
44 | clients.inMemory()
45 | .withClient("merryyou1")
46 | .secret("merryyousecrect1")
47 | .authorizedGrantTypes("authorization_code", "refresh_token")
48 | .scopes("all")
49 | .and()
50 | .withClient("merryyou2")
51 | .secret("merryyousecrect2")
52 | .authorizedGrantTypes("authorization_code", "refresh_token")
53 | .scopes("all");
54 | }
55 |
56 | /**
57 | * 配置jwttokenStore
58 | * @param endpoints
59 | * @throws Exception
60 | */
61 | @Override
62 | public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
63 | endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter());
64 | }
65 |
66 | /**
67 | * springSecurity 授权表达式,访问merryyou tokenkey时需要经过认证
68 | * @param security
69 | * @throws Exception
70 | */
71 | @Override
72 | public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
73 | security.tokenKeyAccess("isAuthenticated()");
74 | }
75 |
76 | /**
77 | * JWTtokenStore
78 | * @return
79 | */
80 | @Bean
81 | public TokenStore jwtTokenStore() {
82 | return new JwtTokenStore(jwtAccessTokenConverter());
83 | }
84 |
85 | /**
86 | * 生成JTW token
87 | * @return
88 | */
89 | @Bean
90 | public JwtAccessTokenConverter jwtAccessTokenConverter(){
91 | JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
92 | converter.setSigningKey("merryyou");
93 | return converter;
94 | }
95 | }
96 | ```
97 | #### security配置
98 | ```java
99 | @Configuration
100 | public class SsoSecurityConfig extends WebSecurityConfigurerAdapter {
101 |
102 | @Autowired
103 | private UserDetailsService userDetailsService;
104 |
105 | @Bean
106 | public PasswordEncoder passwordEncoder() {
107 | return new BCryptPasswordEncoder();
108 | }
109 |
110 | @Override
111 | protected void configure(HttpSecurity http) throws Exception {
112 | http.formLogin().loginPage("/authentication/require")
113 | .loginProcessingUrl("/authentication/form")
114 | .and().authorizeRequests()
115 | .antMatchers("/authentication/require",
116 | "/authentication/form",
117 | "/**/*.js",
118 | "/**/*.css",
119 | "/**/*.jpg",
120 | "/**/*.png",
121 | "/**/*.woff2"
122 | )
123 | .permitAll()
124 | .anyRequest().authenticated()
125 | .and()
126 | .csrf().disable();
127 | // http.formLogin().and().authorizeRequests().anyRequest().authenticated();
128 | }
129 |
130 | @Override
131 | protected void configure(AuthenticationManagerBuilder auth) throws Exception {
132 | auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
133 | }
134 | }
135 | ```
136 | #### SsoUserDetailsService
137 | ```java
138 | @Component
139 | public class SsoUserDetailsService implements UserDetailsService {
140 |
141 | @Autowired
142 | private PasswordEncoder passwordEncoder;
143 |
144 | @Override
145 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
146 | return new User(username, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
147 | }
148 | }
149 | ```
150 | #### application.yml
151 | ```yaml
152 | server:
153 | port: 8082
154 | context-path: /uaa
155 | spring:
156 | freemarker:
157 | allow-request-override: false
158 | allow-session-override: false
159 | cache: true
160 | charset: UTF-8
161 | check-template-location: true
162 | content-type: text/html
163 | enabled: true
164 | expose-request-attributes: false
165 | expose-session-attributes: false
166 | expose-spring-macro-helpers: true
167 | prefer-file-system-access: true
168 | suffix: .ftl
169 | template-loader-path: classpath:/templates/
170 | ```
171 | ### sso-client1
172 | #### SsoClient1Application
173 | ```java
174 | @SpringBootApplication
175 | @RestController
176 | @EnableOAuth2Sso
177 | public class SsoClient1Application {
178 |
179 | @GetMapping("/user")
180 | public Authentication user(Authentication user) {
181 | return user;
182 | }
183 |
184 | public static void main(String[] args) {
185 | SpringApplication.run(SsoClient1Application.class, args);
186 | }
187 | }
188 | ```
189 | #### application.yml
190 | ```java
191 | auth-server: http://localhost:8082/uaa # sso-server地址
192 | server:
193 | context-path: /client1
194 | port: 8083
195 | security:
196 | oauth2:
197 | client:
198 | client-id: merryyou1
199 | client-secret: merryyousecrect1
200 | user-authorization-uri: ${auth-server}/oauth/authorize #请求认证的地址
201 | access-token-uri: ${auth-server}/oauth/token #请求令牌的地址
202 | resource:
203 | jwt:
204 | key-uri: ${auth-server}/oauth/token_key #解析jwt令牌所需要密钥的地址
205 | ```
206 | ### sso-client2
207 | #### 同sso-client1一致
208 |
209 | 效果如下:
210 | [](https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth2-sso01.gif "https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth2-sso01.gif")
211 |
212 | ## 启动方式
213 | 1. 启动sso-server
214 | 2. 启动sso-client1
215 | 3. 启动sso-client2
216 | 4. http://localhost:8083/client1/ 用户名随意,密码123456
217 | 5. http://localhost:8083/client1/user 查看当前的用户信息
218 |
219 | ## update2018年01月28日
220 | 增加sso-resource(支持sso-client1资源服务器)
221 | ## update2018年02月01日
222 | 由[laungcisin](https://github.com/laungcisin)提供`SsoAuthorizationServerConfig`自动授权配置`autoApprove(true)`
223 | ## update2018年04月19日
224 | 添加`docker-compose`启动方式,需在本地host文件中添加`127.0.0.1 sso-login sso-taobao sso-tmall sso-resource`.访问地址为:[http://sso-taobao:8083/client1](http://sso-taobao:8083/client1)
225 | ## update2018年04月22日
226 | 添加[SpringBoot+Docker+Git+Jenkins实现简易的持续集成和持续部署](https://longfeizheng.github.io/2018/04/22/SpringBoot+Docker+Git+Jenkins%E5%AE%9E%E7%8E%B0%E7%AE%80%E6%98%93%E7%9A%84%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90%E5%92%8C%E9%83%A8%E7%BD%B2/)
227 | ## update2018年05月09日
228 | 升级[springboot2.0单点登录](https://github.com/longfeizheng/springboot2.0-sso-merryyou)
229 |
230 | ---
231 | [](https://niocoder.com/assets/images/qrcode.jpg "https://niocoder.com/assets/images/qrcode.jpg")
232 |
233 | > 🙂🙂🙂关注微信公众号**java干货**
234 | 不定期分享干货资料
235 |
236 | ## Start统计
237 |
238 | [](https://starcharts.herokuapp.com/longfeizheng/sso-merryyou)
239 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | sso-login:
4 | image: hub.c.163.com/longfeizheng/sso-server:1.0
5 | restart: always
6 | ports:
7 | - "8082:8082"
8 | # command:
9 | # - "--server.port=8082"
10 | sso-taobao:
11 | image: hub.c.163.com/longfeizheng/sso-client1:1.0
12 | restart: always
13 | ports:
14 | - 8083:8083
15 | links:
16 | - sso-login
17 | sso-tmall:
18 | image: hub.c.163.com/longfeizheng/sso-client2:1.0
19 | restart: always
20 | ports:
21 | - 8084:8084
22 | links:
23 | - sso-login
24 | sso-resource:
25 | image: hub.c.163.com/longfeizheng/sso-resource:1.0
26 | restart: always
27 | ports:
28 | - 8085:8085
29 | links:
30 | - sso-login
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
t |