SpringSecurityOAuth使用JWT Token实现SSO单点登录

⒈认证服务器

  1.添加pom依赖   

 1         <dependency>
 2             <groupId>org.springframework.boot</groupId>
 3             <artifactId>spring-boot-starter-web</artifactId>
 4         </dependency>
 5 
 6         <dependency>
 7             <groupId>org.springframework.cloud</groupId>
 8             <artifactId>spring-cloud-starter-oauth2</artifactId>
 9             <version>2.1.2.RELEASE</version>
10         </dependency>
11 
12         <dependency>
13             <groupId>org.springframework.boot</groupId>
14             <artifactId>spring-boot-starter-test</artifactId>
15             <scope>test</scope>
16         </dependency>
17         <dependency>
18             <groupId>org.springframework.security</groupId>
19             <artifactId>spring-security-test</artifactId>
20             <scope>test</scope>
21         </dependency>

  2.配置文件相关配置  

1 server.port=10086
2 server.servlet.context-path=/server

  3.Security配置

 1 package cn.coreqi.ssoserver.config;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.beans.factory.annotation.Qualifier;
 5 import org.springframework.context.annotation.Bean;
 6 import org.springframework.security.authentication.AuthenticationManager;
 7 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 8 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 9 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
10 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
11 import org.springframework.security.core.userdetails.UserDetailsService;
12 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
13 import org.springframework.security.crypto.factory.PasswordEncoderFactories;
14 import org.springframework.security.crypto.password.NoOpPasswordEncoder;
15 import org.springframework.security.crypto.password.PasswordEncoder;
16 
17 @EnableWebSecurity
18 public class SsoWebSecurityConfig extends WebSecurityConfigurerAdapter {
19 
20     @Override
21     @Bean
22     public AuthenticationManager authenticationManagerBean() throws Exception {
23         return super.authenticationManagerBean();
24     }
25 
26     @Qualifier("ssoUserDetailsService")
27     @Autowired
28     private UserDetailsService userDetailsService;
29 
30     @Bean
31     public PasswordEncoder passwordEncoder()
32     {
33         //return NoOpPasswordEncoder.getInstance();
34         //return new BCryptPasswordEncoder();
35         return PasswordEncoderFactories.createDelegatingPasswordEncoder();
36     }
37 
38     @Override
39     protected void configure(HttpSecurity http) throws Exception {
40         http.formLogin()
41                 .and()
42                 .authorizeRequests()
43                 .antMatchers("/oauth/*","/login/*").permitAll()
44                 .anyRequest().authenticated()  //任何请求都需要身份认证
45                 .and().csrf().disable();    //禁用CSRF
46     }
47 
48     @Override
49     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
50 //        auth.inMemoryAuthentication()
51 //                .withUser("fanqi").password("admin").roles("admin");
52         auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
53     }
54 }

  ⒋用户登录逻辑

  

 1 package cn.coreqi.ssoserver.service;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.security.core.authority.AuthorityUtils;
 5 import org.springframework.security.core.userdetails.User;
 6 import org.springframework.security.core.userdetails.UserDetails;
 7 import org.springframework.security.core.userdetails.UserDetailsService;
 8 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 9 import org.springframework.security.crypto.password.PasswordEncoder;
10 import org.springframework.stereotype.Component;
11 
12 @Component
13 public class SsoUserDetailsService implements UserDetailsService {
14 
15     @Autowired
16     private PasswordEncoder passwordEncoder;
17 
18     @Override
19     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
20         return new User(username,passwordEncoder.encode("admin"),
21                 AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN"));
22     }
23 }

  5.认证服务器配置

 1 package cn.coreqi.ssoserver.config;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.context.annotation.Bean;
 5 import org.springframework.context.annotation.Configuration;
 6 import org.springframework.security.authentication.AuthenticationManager;
 7 import org.springframework.security.crypto.password.PasswordEncoder;
 8 import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
 9 import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
10 import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
11 import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
12 import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
13 import org.springframework.security.oauth2.provider.token.TokenStore;
14 import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
15 import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
16 
17 @Configuration
18 @EnableAuthorizationServer  //声明当前应用为认证服务器
19 public class SsoAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
20 
21     @Autowired
22     private AuthenticationManager authenticationManagerBean;
23 
24     @Autowired
25     private PasswordEncoder passwordEncoder;
26 
27     /**
28      * Token生成过程处理
29      * @return
30      */
31     @Bean
32     public JwtAccessTokenConverter jwtAccessTokenConverter(){
33         JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
34         accessTokenConverter.setSigningKey("fanqi");   //Token签名用的密钥
35         //发出去的令牌需要密钥签名,验令牌的时候也需要令牌来验签,如果他人获知了我们的密钥
36         //就可以用我们的密钥来签发我们的JWT令牌,JWT唯一的安全性就是密钥
37         //别人用我们的密钥来签发我们的JWT令牌就可以随意进入我们的系统
38         return accessTokenConverter;
39     }
40 
41     @Bean
42     public TokenStore jwtTokenStore(){
43         return new JwtTokenStore(jwtAccessTokenConverter());
44     }
45 
46     /**
47      * 针对端点的配置
48      * @param endpoints
49      * @throws Exception
50      */
51     @Override
52     public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
53         endpoints
54                 .tokenStore(jwtTokenStore())
55                 .accessTokenConverter(jwtAccessTokenConverter())
56                 .authenticationManager(authenticationManagerBean);
57     }
58 
59     /**
60      * 配置当前认证服务器可以给那些应用发令牌
61      * @param clients
62      * @throws Exception
63      */
64     @Override
65     public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
66 
67         clients.inMemory()
68                 .withClient("coreqi1")
69                 .secret(passwordEncoder.encode("coreqisecret1"))
70                 .authorizedGrantTypes("authorization_code","refresh_token")
71                 .redirectUris("http://localhost:10010/client1/login","http://127.0.0.1:10010/client1/login")
72                 .scopes("all")
73             .and()
74                 .withClient("coreqi2")
75                 .secret(passwordEncoder.encode("coreqisecret2"))
76                 .authorizedGrantTypes("authorization_code","refresh_token")
77                 .redirectUris("http://localhost:10000/client2/login","http://127.0.0.1:10000/client2/login")
78                 .scopes("all");
79     }
80 
81     /**
82      * 针对安全性有关的配置
83      * @param security
84      * @throws Exception
85      */
86     @Override
87     public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
88         security
89                 .tokenKeyAccess("isAuthenticated()");   //访问认证服务器的tokenKey(Token签名密钥)时需要身份认证
90     }
91 }

  6.覆写登录授权页面,直接跳过

  1 package cn.coreqi.ssoserver.controller;
  2 
  3 import org.springframework.security.oauth2.provider.AuthorizationRequest;
  4 import org.springframework.security.web.csrf.CsrfToken;
  5 import org.springframework.web.bind.annotation.RequestMapping;
  6 import org.springframework.web.bind.annotation.RestController;
  7 import org.springframework.web.bind.annotation.SessionAttributes;
  8 import org.springframework.web.servlet.ModelAndView;
  9 import org.springframework.web.servlet.View;
 10 import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
 11 import org.springframework.web.util.HtmlUtils;
 12 
 13 import javax.servlet.http.HttpServletRequest;
 14 import javax.servlet.http.HttpServletResponse;
 15 import java.util.Iterator;
 16 import java.util.Map;
 17 
 18 /**
 19  * 登录完不授权直接进入网站
 20  * 授权的页面根据OAuth协议是无法直接跳过去的
 21  * 因此,我们模仿WhitelabelApprovalEndpoint类,在表单逻辑处直接提交
 22  */
 23 @RestController
 24 @SessionAttributes({"authorizationRequest"})
 25 public class SsoApprovalEndpoint {
 26     @RequestMapping({"/oauth/confirm_access"})
 27     public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
 28         final String approvalContent = this.createTemplate(model, request);
 29         if (request.getAttribute("_csrf") != null) {
 30             model.put("_csrf", request.getAttribute("_csrf"));
 31         }
 32 
 33         View approvalView = new View() {
 34             public String getContentType() {
 35                 return "text/html";
 36             }
 37 
 38             public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
 39                 response.setContentType(this.getContentType());
 40                 response.getWriter().append(approvalContent);
 41             }
 42         };
 43         return new ModelAndView(approvalView, model);
 44     }
 45 
 46     protected String createTemplate(Map<String, Object> model, HttpServletRequest request) {
 47         AuthorizationRequest authorizationRequest = (AuthorizationRequest)model.get("authorizationRequest");
 48         String clientId = authorizationRequest.getClientId();
 49         StringBuilder builder = new StringBuilder();
 50         builder.append("<html><body><div style='display:none'><h1>OAuth Approval</h1>");
 51         builder.append("<p>Do you authorize \"").append(HtmlUtils.htmlEscape(clientId));
 52         builder.append("\" to access your protected resources?</p>");
 53         builder.append("<form id=\"confirmationForm\" name=\"confirmationForm\" action=\"");
 54         String requestPath = ServletUriComponentsBuilder.fromContextPath(request).build().getPath();
 55         if (requestPath == null) {
 56             requestPath = "";
 57         }
 58 
 59         builder.append(requestPath).append("/oauth/authorize\" method=\"post\">");
 60         builder.append("<input name=\"user_oauth_approval\" value=\"true\" type=\"hidden\"/>");
 61         String csrfTemplate = null;
 62         CsrfToken csrfToken = (CsrfToken)((CsrfToken)(model.containsKey("_csrf") ? model.get("_csrf") : request.getAttribute("_csrf")));
 63         if (csrfToken != null) {
 64             csrfTemplate = "<input type=\"hidden\" name=\"" + HtmlUtils.htmlEscape(csrfToken.getParameterName()) + "\" value=\"" + HtmlUtils.htmlEscape(csrfToken.getToken()) + "\" />";
 65         }
 66 
 67         if (csrfTemplate != null) {
 68             builder.append(csrfTemplate);
 69         }
 70 
 71         String authorizeInputTemplate = "<label><input name=\"authorize\" value=\"Authorize\" type=\"submit\"/></label></form>";
 72         if (!model.containsKey("scopes") && request.getAttribute("scopes") == null) {
 73             builder.append(authorizeInputTemplate);
 74             builder.append("<form id=\"denialForm\" name=\"denialForm\" action=\"");
 75             builder.append(requestPath).append("/oauth/authorize\" method=\"post\">");
 76             builder.append("<input name=\"user_oauth_approval\" value=\"false\" type=\"hidden\"/>");
 77             if (csrfTemplate != null) {
 78                 builder.append(csrfTemplate);
 79             }
 80 
 81             builder.append("<label><input name=\"deny\" value=\"Deny\" type=\"submit\"/></label></form>");
 82         } else {
 83             builder.append(this.createScopes(model, request));
 84             builder.append(authorizeInputTemplate);
 85         }
 86 
 87         builder.append("</div><script>document.getElementById('confirmationForm').submit()</script></body></html>");
 88         return builder.toString();
 89     }
 90 
 91     private CharSequence createScopes(Map<String, Object> model, HttpServletRequest request) {
 92         StringBuilder builder = new StringBuilder("<ul>");
 93         Map<String, String> scopes = (Map)((Map)(model.containsKey("scopes") ? model.get("scopes") : request.getAttribute("scopes")));
 94         Iterator var5 = scopes.keySet().iterator();
 95 
 96         while(var5.hasNext()) {
 97             String scope = (String)var5.next();
 98             String approved = "true".equals(scopes.get(scope)) ? " checked" : "";
 99             String denied = !"true".equals(scopes.get(scope)) ? " checked" : "";
100             scope = HtmlUtils.htmlEscape(scope);
101             builder.append("<li><div class=\"form-group\">");
102             builder.append(scope).append(": <input type=\"radio\" name=\"");
103             builder.append(scope).append("\" value=\"true\"").append(approved).append(">Approve</input> ");
104             builder.append("<input type=\"radio\" name=\"").append(scope).append("\" value=\"false\"");
105             builder.append(denied).append(">Deny</input></div></li>");
106         }
107 
108         builder.append("</ul>");
109         return builder.toString();
110     }
111 
112 }

 

⒉应用A

  1.pom依赖

    

 1         <dependency>
 2             <groupId>org.springframework.boot</groupId>
 3             <artifactId>spring-boot-starter-web</artifactId>
 4         </dependency>
 5 
 6         <dependency>
 7             <groupId>org.springframework.cloud</groupId>
 8             <artifactId>spring-cloud-starter-oauth2</artifactId>
 9             <version>2.1.2.RELEASE</version>
10         </dependency>
11 
12         <dependency>
13             <groupId>org.springframework.boot</groupId>
14             <artifactId>spring-boot-starter-test</artifactId>
15             <scope>test</scope>
16         </dependency>
17         <dependency>
18             <groupId>org.springframework.security</groupId>
19             <artifactId>spring-security-test</artifactId>
20             <scope>test</scope>
21         </dependency>

  2.配置文件相关配置

1 server.port=10010
2 server.servlet.context-path=/client1
3 security.oauth2.client.client-id=coreqi1
4 security.oauth2.client.client-secret=coreqisecret1
5 security.oauth2.client.scope=all
6 security.oauth2.client.user-authorization-uri=http://127.0.0.1:10086/server/oauth/authorize
7 security.oauth2.client.access-token-uri=http://127.0.0.1:10086/server/oauth/token
8 security.oauth2.resource.jwt.key-uri=http://127.0.0.1:10086/server/oauth/token_key

  3.主程序类添加@EnableOAuth2Sso注解使之生效

 1 package cn.coreqi;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
 6 
 7 @SpringBootApplication
 8 @EnableOAuth2Sso    //使sso生效
 9 public class SsoClient1Application {
10 
11     public static void main(String[] args) {
12         SpringApplication.run(SsoClient1Application.class, args);
13     }
14 
15 }

  4.编写Action接口用于查看授权信息

 1 package cn.coreqi.sso_client1.controller;
 2 
 3 import org.springframework.security.core.Authentication;
 4 import org.springframework.web.bind.annotation.GetMapping;
 5 import org.springframework.web.bind.annotation.RequestMapping;
 6 import org.springframework.web.bind.annotation.RestController;
 7 
 8 @RestController
 9 @RequestMapping("/user")
10 public class UserController {
11 
12     @GetMapping
13     public Authentication user(Authentication user){
14         return user;
15     }
16 }

  5.编写resources/static/index.html文件,用于跳转测试

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>SSO Client1</title>
 6 </head>
 7 <body>
 8     <h1> SSO Demo Client1</h1>
 9     <a href="http://127.0.0.1:10000/client2/index.html">访问Client2</a>
10 </body>
11 </html>

 

⒊应用B

  1.添加pom依赖

 1         <dependency>
 2             <groupId>org.springframework.boot</groupId>
 3             <artifactId>spring-boot-starter-web</artifactId>
 4         </dependency>
 5 
 6         <dependency>
 7             <groupId>org.springframework.cloud</groupId>
 8             <artifactId>spring-cloud-starter-oauth2</artifactId>
 9             <version>2.1.2.RELEASE</version>
10         </dependency>
11 
12         <dependency>
13             <groupId>org.springframework.boot</groupId>
14             <artifactId>spring-boot-starter-test</artifactId>
15             <scope>test</scope>
16         </dependency>
17         <dependency>
18             <groupId>org.springframework.security</groupId>
19             <artifactId>spring-security-test</artifactId>
20             <scope>test</scope>
21         </dependency>

  2.配置文件相关配置

1 server.port=10000
2 server.servlet.context-path=/client2
3 security.oauth2.client.client-id=coreqi2
4 security.oauth2.client.client-secret=coreqisecret2
5 security.oauth2.client.scope=all
6 security.oauth2.client.user-authorization-uri=http://127.0.0.1:10086/server/oauth/authorize
7 security.oauth2.client.access-token-uri=http://127.0.0.1:10086/server/oauth/token
8 security.oauth2.resource.jwt.key-uri=http://127.0.0.1:10086/server/oauth/token_key

  3.主程序类添加@EnableOAuth2Sso注解使之生效

 1 package cn.coreqi;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
 6 
 7 @SpringBootApplication
 8 @EnableOAuth2Sso    //使sso生效
 9 public class SsoClient2Application {
10 
11     public static void main(String[] args) {
12         SpringApplication.run(SsoClient2Application.class, args);
13     }
14 
15 }

  4.编写Action接口用于查看授权信息

 1 package cn.coreqi.sso_client2.controller;
 2 
 3 import org.springframework.security.core.Authentication;
 4 import org.springframework.web.bind.annotation.GetMapping;
 5 import org.springframework.web.bind.annotation.RequestMapping;
 6 import org.springframework.web.bind.annotation.RestController;
 7 
 8 @RestController
 9 @RequestMapping("/user")
10 public class UserController {
11 
12     @GetMapping
13     public Authentication user(Authentication user){
14         return user;
15     }
16 }

  5.编写resources/static/index.html文件,用于跳转测试

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>SSO Client2</title>
 6 </head>
 7 <body>
 8     <h1> SSO Demo Client2</h1>
 9     <a href="http://127.0.0.1:10010/client1/index.html">访问Client1</a>
10 </body>
11 </html>

 

posted @ 2019-04-09 21:25  SpringCore  阅读(3332)  评论(0编辑  收藏  举报