├── web
├── favicon.ico
├── login.html
├── index.html
└── js
│ ├── jquery.jsonview.css
│ ├── jquery.jsonview.js
│ └── jquery-3.1.1.min.js
├── .gitignore
├── spring-security-oauth2-client
├── .gitignore
├── src
│ └── main
│ │ ├── java
│ │ └── red
│ │ │ └── zyc
│ │ │ └── spring
│ │ │ └── security
│ │ │ └── oauth2
│ │ │ └── client
│ │ │ ├── SpringSecurityOauth2ClientApplication.java
│ │ │ ├── security
│ │ │ ├── CustomizedAccessDeniedHandler.java
│ │ │ ├── CustomizedAuthenticationEntryPoint.java
│ │ │ ├── Oauth2AuthenticationSuccessHandler.java
│ │ │ ├── Oauth2AuthenticationFailureHandler.java
│ │ │ └── CustomizedOauth2UserService.java
│ │ │ ├── controller
│ │ │ └── Oath2Controller.java
│ │ │ └── config
│ │ │ └── WebSecurityConfig.java
│ │ └── resources
│ │ └── application.yml
├── README.md
└── pom.xml
├── spring-security-oauth2-resourceserver
├── .gitignore
├── src
│ └── main
│ │ ├── resources
│ │ ├── application.yml
│ │ ├── key.public
│ │ └── key.private
│ │ └── java
│ │ └── red
│ │ └── zyc
│ │ └── spring
│ │ └── security
│ │ └── oauth2
│ │ └── resourceserver
│ │ ├── SpringSecurityOauth2ResourceServerApplication.java
│ │ ├── controller
│ │ └── UserController.java
│ │ └── JwtUtil.java
├── README.md
└── pom.xml
├── README.md
├── nginx.conf
└── pom.xml
/web/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/allurx/spring-security-oauth2-demo/HEAD/web/favicon.ico
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # IntelliJ project files
2 | .idea
3 | *.iml
4 |
5 | # java
6 | target
7 |
8 | # jrebel
9 | rebel.xml
--------------------------------------------------------------------------------
/spring-security-oauth2-client/.gitignore:
--------------------------------------------------------------------------------
1 | # IntelliJ project files
2 | .idea
3 | *.iml
4 |
5 | # java
6 | target
7 |
8 | # jrebel
9 | rebel.xml
--------------------------------------------------------------------------------
/spring-security-oauth2-resourceserver/.gitignore:
--------------------------------------------------------------------------------
1 | # IntelliJ project files
2 | .idea
3 | *.iml
4 |
5 | # java
6 | target
7 |
8 | # jrebel
9 | rebel.xml
--------------------------------------------------------------------------------
/spring-security-oauth2-resourceserver/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | security:
3 | oauth2:
4 | resourceserver:
5 | jwt:
6 | public-key-location: classpath:key.public
7 | jws-algorithm: RS512
8 | logging:
9 | level:
10 | org.springframework.security: debug
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # spring-security-oauth2-demo
2 | 1. [集成GitHub和QQ社交登录](https://github.com/Allurx/spring-security-oauth2-demo/tree/master/spring-security-oauth2-client)
3 | 2. [使用jwt令牌访问受保护的资源](https://github.com/Allurx/spring-security-oauth2-demo/tree/master/spring-security-oauth2-resourceserver)
4 | 3. 搭建授权服务器([等待spring-security支持](https://spring.io/blog/2020/04/15/announcing-the-spring-authorization-server))
5 |
--------------------------------------------------------------------------------
/web/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 登录
6 |
7 |
12 |
13 | 使用GitHub登录
14 | 使用QQ登录
15 |
16 |
--------------------------------------------------------------------------------
/spring-security-oauth2-resourceserver/src/main/resources/key.public:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxsd1PJN/vf/Zi8PnY3XzBxURBUHuSl0G
3 | XjlqHq7+lNeJ//eDSQfhRS1TqStXUBoje0MqgXhw0FkUVQQxcPGLqj+jngRWf8hZtfDg4RYKLOO7
4 | tHL6na7c8HYkvhblxhZHDOswnj+ZBkjmfCUzyzK6eqdwMMwqZhpCcWhtcluaQ3k2lT71gI0nUfFs
5 | NGE+vMJlepbA+dGXcmFbh401mw0Bk5JWjL3VAdmHK8+dzAXFoZ6+X6S9xdOIXtev3LaeGkwhLfK4
6 | Qpri5rKcSt2q3PfbMLPd22UDtu1tTGOXo8Mt+tTOUyHAp2qKMJ03e9KXnbu4tROlKBhLyFunyHkf
7 | nhOmSwIDAQAB
8 | -----END PUBLIC KEY-----
--------------------------------------------------------------------------------
/nginx.conf:
--------------------------------------------------------------------------------
1 | #user nobody;
2 | worker_processes 1;
3 |
4 | events {
5 | worker_connections 1024;
6 | }
7 |
8 | http {
9 |
10 | include mime.types;
11 | default_type application/octet-stream;
12 | sendfile on;
13 | keepalive_timeout 65;
14 |
15 | server {
16 | listen 80;
17 | server_name localhost;
18 | # 修改为你自己的web工程所在的目标
19 | root your web project root directory;
20 | index login.html;
21 | location /api {
22 | proxy_pass http://localhost:9000;
23 | }
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/spring-security-oauth2-client/src/main/java/red/zyc/spring/security/oauth2/client/SpringSecurityOauth2ClientApplication.java:
--------------------------------------------------------------------------------
1 | package red.zyc.spring.security.oauth2.client;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | /**
7 | * @author zyc
8 | */
9 | @SpringBootApplication
10 | public class SpringSecurityOauth2ClientApplication {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(SpringSecurityOauth2ClientApplication.class, args);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/spring-security-oauth2-resourceserver/src/main/java/red/zyc/spring/security/oauth2/resourceserver/SpringSecurityOauth2ResourceServerApplication.java:
--------------------------------------------------------------------------------
1 | package red.zyc.spring.security.oauth2.resourceserver;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | /**
7 | * @author zyc
8 | */
9 | @SpringBootApplication
10 | public class SpringSecurityOauth2ResourceServerApplication {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(SpringSecurityOauth2ResourceServerApplication.class, args);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/spring-security-oauth2-resourceserver/README.md:
--------------------------------------------------------------------------------
1 | # 使用jwt令牌访问受保护的资源
2 | ## 准备
3 | 1. 使用[JwtUtil](https://github.com/Allurx/spring-security-oauth2-demo/blob/master/spring-security-oauth2-resourceserver/src/main/java/red/zyc/spring/security/oauth2/resourceserver/JwtUtil.java)
4 | 的`keys`方法生成RSA公钥和私钥,然后分别保存为key.private和key.public文件存放到工程的resource文件夹下
5 | 2. 使用[JwtUtil](https://github.com/Allurx/spring-security-oauth2-demo/blob/master/spring-security-oauth2-resourceserver/src/main/java/red/zyc/spring/security/oauth2/resourceserver/JwtUtil.java)
6 | 的`jwt`方法生成一个jwt令牌
7 |
8 | 这里的1、2两个步骤在实际使用时应该由授权服务器来执行,即授权服务器生成jwt令牌返回给客户端。这里我们为了方便演示,所以手动模拟了授权服务器生成令牌的过程。
9 | ## 开始
10 | 启动工程,在请求头中带上刚刚生成的jwt令牌访问`localhost:8080/`
11 | ```
12 | Authorization: Bearer jwt令牌
13 | ```
14 | 即可在控制台看到此jwt令牌所携带的用户信息
15 | ## 参考
16 | [Spring-Security-OAuth2-Resource-Server原理](https://www.zyc.red/Spring/Security/OAuth2/OAuth2-Resource-Server/)
17 |
--------------------------------------------------------------------------------
/spring-security-oauth2-client/README.md:
--------------------------------------------------------------------------------
1 | # 集成GitHub和QQ社交登录
2 | ## 准备
3 | 1. [本机安装nginx](http://nginx.org/en/download.html)
4 |
5 | 将[nginx.conf](https://github.com/Allurx/spring-security-oauth2-demo/blob/master/nginx.conf)文件添加到你的nginx配置下,修改root节点值为你本地[web工程](https://github.com/Allurx/spring-security-oauth2-demo/tree/master/web)所在的目录
6 | 2. [创建一个GitHub OAuth App](https://github.com/settings/developers)
7 | 3. [创建一个QQ网站应用](https://connect.qq.com)
8 | ## 开始
9 | 1. 修改配置文件
10 | 1. 将spring-security-oauth2-client工程`application.yml`文件中`registration.github`和`registration.qq`属性下的clientId、clientSecret替换为你自己GitHub OAuth App和QQ网站应用的值
11 | 2. 将[登录页](https://github.com/Allurx/spring-security-oauth2-demo/blob/master/web/login.html)script脚本中的data-appid值替换为你自己的QQ网站应用APP ID
12 | 2. 启动nginx和spring-security-oauth2-client工程,分别点击GitHub和QQ登录即可跳转三方授权登录页,最终在首页会显示已认证的用户和客户端信息。
13 | ## 参考
14 | [Spring-Security-OAuth2-Client原理](https://www.zyc.red/Spring/Security/OAuth2/OAuth2-Client/)
15 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | pom
6 |
7 | org.springframework.boot
8 | spring-boot-starter-parent
9 | 2.2.6.RELEASE
10 |
11 | red.zyc
12 | spring-security-oauth2-demo
13 | 0.0.1-SNAPSHOT
14 | spring-security-oauth2-demo
15 | spring-security-oauth2-demo
16 |
17 |
18 | 1.8
19 |
20 |
21 |
22 |
23 | org.projectlombok
24 | lombok
25 | true
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/spring-security-oauth2-client/src/main/java/red/zyc/spring/security/oauth2/client/security/CustomizedAccessDeniedHandler.java:
--------------------------------------------------------------------------------
1 | package red.zyc.spring.security.oauth2.client.security;
2 |
3 |
4 | import org.springframework.security.access.AccessDeniedException;
5 | import org.springframework.security.web.access.AccessDeniedHandler;
6 | import org.springframework.security.web.access.ExceptionTranslationFilter;
7 |
8 | import javax.servlet.http.HttpServletRequest;
9 | import javax.servlet.http.HttpServletResponse;
10 | import java.io.IOException;
11 | import java.nio.charset.StandardCharsets;
12 |
13 |
14 | /**
15 | * 接口无权访问处理器
16 | * {@link ExceptionTranslationFilter#handleSpringSecurityException}
17 | *
18 | * @author zyc
19 | */
20 | public class CustomizedAccessDeniedHandler implements AccessDeniedHandler {
21 |
22 | @Override
23 | public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
24 | response.setCharacterEncoding(StandardCharsets.UTF_8.name());
25 | response.setContentType("text/plain");
26 | response.getWriter().write("用户未授权");
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/spring-security-oauth2-client/src/main/java/red/zyc/spring/security/oauth2/client/security/CustomizedAuthenticationEntryPoint.java:
--------------------------------------------------------------------------------
1 | package red.zyc.spring.security.oauth2.client.security;
2 |
3 |
4 | import org.springframework.security.core.AuthenticationException;
5 | import org.springframework.security.web.AuthenticationEntryPoint;
6 | import org.springframework.security.web.access.ExceptionTranslationFilter;
7 |
8 | import javax.servlet.http.HttpServletRequest;
9 | import javax.servlet.http.HttpServletResponse;
10 | import java.io.IOException;
11 | import java.nio.charset.StandardCharsets;
12 |
13 |
14 | /**
15 | * 接口需要特定的权限,但是当前用户是匿名用户或者是记住我的用户
16 | * {@link ExceptionTranslationFilter#handleSpringSecurityException}
17 | *
18 | * @author zyc
19 | */
20 | public class CustomizedAuthenticationEntryPoint implements AuthenticationEntryPoint {
21 |
22 | @Override
23 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
24 | response.setCharacterEncoding(StandardCharsets.UTF_8.name());
25 | response.setContentType("text/plain");
26 | response.getWriter().write("用户未认证");
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
9 |
27 |
28 |
29 |
30 |
用户信息:
31 |
32 |
33 |
34 |
35 |
36 |
37 |
认证客户端信息:
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/spring-security-oauth2-client/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 9000
3 | spring:
4 | security:
5 | oauth2:
6 | client:
7 | provider:
8 | qq:
9 | authorizationUri: https://graph.qq.com/oauth2.0/authorize
10 | tokenUri: https://graph.qq.com/oauth2.0/token
11 | userInfoUri: https://graph.qq.com/user/get_user_info
12 | userNameAttribute: nickname
13 | registration:
14 | github:
15 | clientId: clientId
16 | clientSecret: clientSecret
17 | redirectUri: http://localhost/api/login/oauth2/code/github
18 | qq:
19 | clientId: clientId
20 | clientSecret: clientSecret
21 | # 在本地测试时需要配置代理将自己在qq注册的redirectUri重定向为http://localhost/api/login/oauth2/code/qq
22 | redirectUri: https://www.zyc.red/api/login/oauth2/code/qq
23 | authorizationGrantType: authorization_code
24 | # 授权码模式需要传递code、state、client_id、client_secret等参数,OAuth2AuthorizationCodeGrantRequestEntityConverter
25 | # 会根据当前的认证方式传递不同的参数
26 | clientAuthenticationMethod: post
27 | logging:
28 | level:
29 | org.springframework.security: debug
30 |
31 |
--------------------------------------------------------------------------------
/spring-security-oauth2-resourceserver/src/main/java/red/zyc/spring/security/oauth2/resourceserver/controller/UserController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package red.zyc.spring.security.oauth2.resourceserver.controller;
18 |
19 | import org.springframework.security.core.annotation.AuthenticationPrincipal;
20 | import org.springframework.security.oauth2.jwt.Jwt;
21 | import org.springframework.web.bind.annotation.GetMapping;
22 | import org.springframework.web.bind.annotation.RestController;
23 |
24 | /**
25 | * @author zyc
26 | */
27 | @RestController
28 | public class UserController {
29 |
30 | @GetMapping
31 | public String index(@AuthenticationPrincipal Jwt jwt) {
32 | return String.format("Hello, %s!", jwt.getSubject());
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/web/js/jquery.jsonview.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | .jsonview {
3 | font-family: monospace;
4 | font-size: 1.1em;
5 | white-space: pre-wrap; }
6 | .jsonview .prop {
7 | font-weight: bold; }
8 | .jsonview .null {
9 | color: red; }
10 | .jsonview .bool {
11 | color: blue; }
12 | .jsonview .num {
13 | color: blue; }
14 | .jsonview .string {
15 | color: green;
16 | white-space: pre-wrap; }
17 | .jsonview .string.multiline {
18 | display: inline-block;
19 | vertical-align: text-top; }
20 | .jsonview .collapser {
21 | position: absolute;
22 | left: -1em;
23 | cursor: pointer; }
24 | .jsonview .collapsible {
25 | transition: height 1.2s;
26 | transition: width 1.2s; }
27 | .jsonview .collapsible.collapsed {
28 | height: .8em;
29 | width: 1em;
30 | display: inline-block;
31 | overflow: hidden;
32 | margin: 0; }
33 | .jsonview .collapsible.collapsed:before {
34 | content: "…";
35 | width: 1em;
36 | margin-left: .2em; }
37 | .jsonview .collapser.collapsed {
38 | transform: rotate(0deg); }
39 | .jsonview .q {
40 | display: inline-block;
41 | width: 0px;
42 | color: transparent; }
43 | .jsonview li {
44 | position: relative; }
45 | .jsonview ul {
46 | list-style: none;
47 | margin: 0 0 0 2em;
48 | padding: 0; }
49 | .jsonview h1 {
50 | font-size: 1.2em; }
51 |
52 | /*# sourceMappingURL=jquery.jsonview.css.map */
53 |
--------------------------------------------------------------------------------
/spring-security-oauth2-client/src/main/java/red/zyc/spring/security/oauth2/client/security/Oauth2AuthenticationSuccessHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package red.zyc.spring.security.oauth2.client.security;
18 |
19 | import org.springframework.security.core.Authentication;
20 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
21 |
22 | import javax.servlet.http.HttpServletRequest;
23 | import javax.servlet.http.HttpServletResponse;
24 | import java.io.IOException;
25 |
26 | /**
27 | * @author zyc
28 | */
29 | public class Oauth2AuthenticationSuccessHandler implements AuthenticationSuccessHandler {
30 |
31 |
32 | @Override
33 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
34 | response.sendRedirect("/index.html");
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/spring-security-oauth2-client/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | red.zyc
7 | spring-security-oauth2-demo
8 | 0.0.1-SNAPSHOT
9 |
10 | spring-security-oauth2-client
11 | 0.0.1-SNAPSHOT
12 | spring-security-oauth2-client
13 | spring-security-oauth2-client
14 |
15 |
16 | 1.8
17 |
18 |
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-oauth2-client
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter-security
27 |
28 |
29 | org.springframework.boot
30 | spring-boot-starter-web
31 |
32 |
33 |
34 |
35 |
36 |
37 | org.springframework.boot
38 | spring-boot-maven-plugin
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/spring-security-oauth2-client/src/main/java/red/zyc/spring/security/oauth2/client/security/Oauth2AuthenticationFailureHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package red.zyc.spring.security.oauth2.client.security;
18 |
19 | import org.springframework.security.core.AuthenticationException;
20 | import org.springframework.security.web.authentication.AuthenticationFailureHandler;
21 |
22 | import javax.servlet.ServletException;
23 | import javax.servlet.http.HttpServletRequest;
24 | import javax.servlet.http.HttpServletResponse;
25 | import java.io.IOException;
26 | import java.nio.charset.StandardCharsets;
27 |
28 | /**
29 | * @author zyc
30 | */
31 | public class Oauth2AuthenticationFailureHandler implements AuthenticationFailureHandler {
32 |
33 | @Override
34 | public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
35 | response.setCharacterEncoding(StandardCharsets.UTF_8.name());
36 | response.setContentType("text/plain");
37 | response.getWriter().write("认证失败");
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/spring-security-oauth2-resourceserver/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | red.zyc
7 | spring-security-oauth2-demo
8 | 0.0.1-SNAPSHOT
9 |
10 | red.zyc
11 | spring-security-oauth2-resourceserver
12 | 0.0.1-SNAPSHOT
13 | spring-security-oauth2-resourceserver
14 | spring-security-oauth2-resourceserver
15 |
16 |
17 | 1.8
18 |
19 |
20 |
21 |
22 | org.springframework.boot
23 | spring-boot-starter-oauth2-resource-server
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-security
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-web
32 |
33 |
34 |
35 |
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-maven-plugin
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/spring-security-oauth2-resourceserver/src/main/resources/key.private:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDGx3U8k3+9/9mLw+djdfMHFREF
3 | Qe5KXQZeOWoerv6U14n/94NJB+FFLVOpK1dQGiN7QyqBeHDQWRRVBDFw8YuqP6OeBFZ/yFm18ODh
4 | Fgos47u0cvqdrtzwdiS+FuXGFkcM6zCeP5kGSOZ8JTPLMrp6p3AwzCpmGkJxaG1yW5pDeTaVPvWA
5 | jSdR8Ww0YT68wmV6lsD50ZdyYVuHjTWbDQGTklaMvdUB2Ycrz53MBcWhnr5fpL3F04he16/ctp4a
6 | TCEt8rhCmuLmspxK3arc99sws93bZQO27W1MY5ejwy361M5TIcCnaoownTd70pedu7i1E6UoGEvI
7 | W6fIeR+eE6ZLAgMBAAECggEBALcFs2ZBEN8qEW3kxMoJMekVdoR2vibuHAzppFH4IiN9iWyKwvCd
8 | NsdxApTCeTQhvQWjRCHNeWH8gwH8SGGLpWLuEYJO0C37lM42qXfVySynyo5NR3+kH32v6gi0IIAQ
9 | xv6YFj2+pPDqcn1f655uaNDCFkR315oHF6I/2nXu7cys0S5menmbIX/zKFZkk+2pbAMCexVbvuB5
10 | wwtd3NX8sCnZDKYOCv/FK8WatAvP3qNv60ApMiza5K+yKjnlP1A6E8mVvUdPKK0NPT0CO0CdEmF6
11 | //1evBQcNgSfEIOakGey7BLCaGkveVCB9ATdwfKIZwo4c+QWL6Kh47T1cD+9cNECgYEA45C3w80s
12 | x0xVFLqvPGA04cVVWNlLdOXMjLV9fcnBUjUCTkmJ4cU8mDybpAM9kz6FYU5g2abDvbOER8V+Sx0W
13 | CyUF90MILoajX/XofDTRBKk0m9eHA0/Q1BdJt8yhfrV8jGz0IUCx/luZR2I+mKEKvUZY0oEDQNDu
14 | rugXYq6OGMUCgYEA353wErjB8pHY9QSH8HuRb5jJTTah3OL492zBaM5g+0690tP2SRAiU9WX55lM
15 | 7N9o9UdqFJza9S0q7NZ5NB9+PgAgvwImkFAvOzPK0NFnfT+DZG22z9TLsZkiXxqDvtKdJdljqr0o
16 | Ymzu8Z+s7g7uuqWsDvyrdQIX4irIGHdrE88CgYBJ+WlDRRchUjb2HhmIzt1h5vvvffOBdJIhy32X
17 | vlYRmxm8yTsBIVSpSEpv7n29t70z/H6PQh6vNAP0MMb1M+dOiCKAVlH6jdnd/9orRiAMG9T2NAG3
18 | meKQj2FvVh3JSsXKAED77kPuI2iYQ9+FThRnos6M31NnZoOwZ9HySjv24QKBgQCDYp6twVRjG4Jn
19 | 47OjflbjRNfxwAm2aL1zUrkIxUmCHq+1ccihARPKQhMwhogGHPXkN4OCfO7BYzp3UUSBdYeNEjIr
20 | SC40WIiHtlSSAJdXpbujhDsHPbY4sQra6g9CTSj8FhBTPzS9L9fsq67FaIynqbPAUoDDDOnPfud2
21 | SKPnTQKBgQDKz6joTogQHoCMgFz7AQ7FQeDgVqxVwz5ugwyiaF35G1nWz3JAfbH8/sQftxo1oH/k
22 | KNyhSwPCQe6Q9hKYtpwlokshAGxAiOcqsaJn6tu7pWW/DkA1gpSNG/NJSeAJljc1YWs5YqnFWiJp
23 | Pj6jYIbz73yja3nt3vMbaHOs9qeXMQ==
24 | -----END PRIVATE KEY-----
--------------------------------------------------------------------------------
/spring-security-oauth2-client/src/main/java/red/zyc/spring/security/oauth2/client/controller/Oath2Controller.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package red.zyc.spring.security.oauth2.client.controller;
18 |
19 | import org.springframework.security.core.annotation.AuthenticationPrincipal;
20 | import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
21 | import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
22 | import org.springframework.security.oauth2.core.user.OAuth2User;
23 | import org.springframework.web.bind.annotation.GetMapping;
24 | import org.springframework.web.bind.annotation.RequestMapping;
25 | import org.springframework.web.bind.annotation.RestController;
26 |
27 | /**
28 | * @author zyc
29 | */
30 | @RequestMapping("/api/oath2")
31 | @RestController
32 | public class Oath2Controller {
33 |
34 | /**
35 | * 获取当前认证的OAuth2用户信息,默认是保存在{@link javax.servlet.http.HttpSession}中的
36 | *
37 | * @param user OAuth2用户信息
38 | * @return OAuth2用户信息
39 | */
40 | @GetMapping("/user")
41 | public OAuth2User user(@AuthenticationPrincipal OAuth2User user) {
42 | return user;
43 | }
44 |
45 | /**
46 | * 获取当前认证的OAuth2客户端信息,默认是保存在{@link javax.servlet.http.HttpSession}中的
47 | *
48 | * @param oAuth2AuthorizedClient OAuth2客户端信息
49 | * @return OAuth2客户端信息
50 | */
51 | @GetMapping("/client")
52 | public OAuth2AuthorizedClient user(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient oAuth2AuthorizedClient) {
53 | return oAuth2AuthorizedClient;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/spring-security-oauth2-resourceserver/src/main/java/red/zyc/spring/security/oauth2/resourceserver/JwtUtil.java:
--------------------------------------------------------------------------------
1 | package red.zyc.spring.security.oauth2.resourceserver;
2 |
3 | import com.nimbusds.jose.JWSAlgorithm;
4 | import com.nimbusds.jose.JWSHeader;
5 | import com.nimbusds.jose.crypto.RSASSASigner;
6 | import com.nimbusds.jwt.JWTClaimsSet;
7 | import com.nimbusds.jwt.SignedJWT;
8 | import lombok.extern.slf4j.Slf4j;
9 | import org.springframework.security.converter.RsaKeyConverters;
10 |
11 | import javax.crypto.Cipher;
12 | import java.security.KeyPair;
13 | import java.security.KeyPairGenerator;
14 | import java.security.PrivateKey;
15 | import java.security.PublicKey;
16 | import java.security.interfaces.RSAPrivateKey;
17 | import java.security.interfaces.RSAPublicKey;
18 | import java.util.Base64;
19 | import java.util.Date;
20 |
21 | /**
22 | * @author zyc
23 | */
24 | @Slf4j
25 | public final class JwtUtil {
26 |
27 | /**
28 | * 私钥
29 | */
30 | private static final RSAPrivateKey PRIVATE_KEY = RsaKeyConverters.pkcs8().convert(JwtUtil.class.getResourceAsStream("/key.private"));
31 |
32 | /**
33 | * 公钥
34 | */
35 | private static final RSAPublicKey PUBLIC_KEY = RsaKeyConverters.x509().convert(JwtUtil.class.getResourceAsStream("/key.public"));
36 |
37 | /**
38 | * rsa算法加解密时的填充方式
39 | */
40 | private static final String RSA_PADDING = "RSA/ECB/PKCS1Padding";
41 |
42 | private JwtUtil() {
43 | }
44 |
45 | /**
46 | * 生成jwt
47 | *
48 | * @return jwt
49 | */
50 | public static String jwt() {
51 | try {
52 | JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder()
53 | .subject("zyc")
54 | .issueTime(new Date())
55 | .build();
56 | SignedJWT jwt = new SignedJWT(new JWSHeader(new JWSAlgorithm("RS512")), jwtClaimsSet);
57 | // 私钥签名,公钥验签
58 | jwt.sign(new RSASSASigner(PRIVATE_KEY));
59 | return jwt.serialize();
60 | } catch (Exception e) {
61 | throw new IllegalStateException(e);
62 | }
63 | }
64 |
65 | /**
66 | * 生成私钥和公钥
67 | */
68 | private static void keys() {
69 | try {
70 | KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
71 | keyPairGen.initialize(2048);
72 | KeyPair keyPair = keyPairGen.generateKeyPair();
73 | PrivateKey privateKey = keyPair.getPrivate();
74 | PublicKey publicKey = keyPair.getPublic();
75 | log.info("{}{}{}", "\n-----BEGIN PRIVATE KEY-----\n", Base64.getMimeEncoder().encodeToString(privateKey.getEncoded()), "\n-----END PRIVATE KEY-----");
76 | log.info("{}{}{}", "\n-----BEGIN PUBLIC KEY-----\n", Base64.getMimeEncoder().encodeToString(publicKey.getEncoded()), "\n-----END PUBLIC KEY-----");
77 | } catch (Exception e) {
78 | throw new IllegalStateException(e);
79 | }
80 | }
81 |
82 | /**
83 | * 加密
84 | *
85 | * @param plaintext 明文
86 | * @return 密文
87 | */
88 | private static String encrypt(String plaintext) {
89 | try {
90 | Cipher cipher = Cipher.getInstance(RSA_PADDING);
91 | cipher.init(Cipher.ENCRYPT_MODE, PUBLIC_KEY);
92 | String encrypt = Base64.getEncoder().encodeToString(cipher.doFinal(plaintext.getBytes()));
93 | log.info("The plaintext {} is encrypted as: {}", plaintext, encrypt);
94 | return encrypt;
95 | } catch (Exception e) {
96 | throw new IllegalStateException(e);
97 | }
98 | }
99 |
100 | /**
101 | * 解密
102 | *
103 | * @param cipherText 密文
104 | * @return 明文
105 | */
106 | private static String decrypt(String cipherText) {
107 | try {
108 | Cipher cipher = Cipher.getInstance(RSA_PADDING);
109 | cipher.init(Cipher.DECRYPT_MODE, PRIVATE_KEY);
110 | String decrypt = new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)));
111 | log.info("The ciphertext {} is decrypted as: {}", cipherText, decrypt);
112 | return decrypt;
113 | } catch (Exception e) {
114 | throw new IllegalStateException(e);
115 | }
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/spring-security-oauth2-client/src/main/java/red/zyc/spring/security/oauth2/client/security/CustomizedOauth2UserService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package red.zyc.spring.security.oauth2.client.security;
18 |
19 | import com.fasterxml.jackson.core.type.TypeReference;
20 | import com.fasterxml.jackson.databind.ObjectMapper;
21 | import lombok.SneakyThrows;
22 | import lombok.extern.slf4j.Slf4j;
23 | import org.springframework.core.ParameterizedTypeReference;
24 | import org.springframework.http.RequestEntity;
25 | import org.springframework.http.ResponseEntity;
26 | import org.springframework.security.core.GrantedAuthority;
27 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
28 | import org.springframework.security.oauth2.client.registration.ClientRegistration;
29 | import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
30 | import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
31 | import org.springframework.security.oauth2.core.OAuth2AccessToken;
32 | import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
33 | import org.springframework.security.oauth2.core.user.OAuth2User;
34 | import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
35 | import org.springframework.web.client.RestTemplate;
36 | import org.springframework.web.util.UriComponentsBuilder;
37 |
38 | import java.util.LinkedHashSet;
39 | import java.util.Map;
40 | import java.util.Objects;
41 | import java.util.Set;
42 |
43 | /**
44 | * @author zyc
45 | */
46 | @Slf4j
47 | public class CustomizedOauth2UserService extends DefaultOAuth2UserService {
48 |
49 | private static final String QQ = "qq";
50 | private static final String QQ_OPEN_ID_URL = "https://graph.qq.com/oauth2.0/me";
51 |
52 | private final RestTemplate restTemplate = new RestTemplate();
53 |
54 | private final ObjectMapper objectMapper = new ObjectMapper();
55 |
56 | @SneakyThrows
57 | @Override
58 | public OAuth2User loadUser(OAuth2UserRequest userRequest) {
59 | ClientRegistration clientRegistration = userRequest.getClientRegistration();
60 | String registrationId = clientRegistration.getRegistrationId();
61 |
62 | // 获取qq用户信息的流程很奇葩需要我们自定义
63 | if (QQ.equals(registrationId)) {
64 |
65 | String tokenValue = userRequest.getAccessToken().getTokenValue();
66 |
67 | // openId请求
68 | RequestEntity> openIdRequest = RequestEntity.get(UriComponentsBuilder.fromUriString(QQ_OPEN_ID_URL).queryParam("access_token", tokenValue)
69 | .build()
70 | .toUri()).build();
71 |
72 | // openId响应
73 | ResponseEntity openIdResponse = restTemplate.exchange(openIdRequest, new ParameterizedTypeReference() {
74 | });
75 |
76 | log.info("qq的openId响应信息:{}", openIdResponse);
77 |
78 | // openId响应是类似callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );这样的字符串
79 | String openId = extractQqOpenId(Objects.requireNonNull(openIdResponse.getBody()));
80 |
81 | // userInfo请求
82 | RequestEntity> userInfoRequest = RequestEntity.get(UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri())
83 | .queryParam("access_token", tokenValue)
84 | .queryParam("openid", openId)
85 | .queryParam("oauth_consumer_key", clientRegistration.getClientId())
86 | .build()
87 | .toUri()).build();
88 |
89 | // userInfo响应
90 | ResponseEntity userInfoResponse = restTemplate.exchange(userInfoRequest, new ParameterizedTypeReference() {
91 | });
92 |
93 | log.info("qq的userInfo响应信息:{}", userInfoResponse);
94 |
95 | String userNameAttributeName = clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
96 | Map userAttributes = extractQqUserInfo(Objects.requireNonNull(userInfoResponse.getBody()));
97 | Set authorities = new LinkedHashSet<>();
98 | authorities.add(new OAuth2UserAuthority(userAttributes));
99 | OAuth2AccessToken token = userRequest.getAccessToken();
100 | for (String authority : token.getScopes()) {
101 | authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
102 | }
103 | return new DefaultOAuth2User(authorities, userAttributes, userNameAttributeName);
104 | }
105 | return super.loadUser(userRequest);
106 | }
107 |
108 |
109 | /**
110 | * 提取qq的openId
111 | *
112 | * @param openIdResponse qq的openId响应字符串
113 | * @return qq的openId
114 | */
115 | @SneakyThrows
116 | private String extractQqOpenId(String openIdResponse) {
117 | String openId = openIdResponse.substring(openIdResponse.indexOf('(') + 1, openIdResponse.indexOf(')'));
118 | Map map = objectMapper.readValue(openId, new TypeReference