热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

SpringSecurity基于JWT实现SSO单点登录详解

这篇文章主要介绍了SpringSecurity基于JWT实现SSO单点登录详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

SSO :同一个帐号在同一个公司不同系统上登陆

这里写图片描述 

使用SpringSecurity实现类似于SSO登陆系统是十分简单的 下面我就搭建一个DEMO

首先来看看目录的结构

这里写图片描述 

其中sso-demo是父工程项目 sso-client 、sso-client2分别对应2个资源服务器,sso-server是认证服务器

引入的pom文件

sso-demo

<&#63;xml version="1.0" encoding="UTF-8"&#63;>

  4.0.0

  study.security.sso
  sso-demo
  1.0.0-SNAPSHOT
  
    sso-server
    sso-client
    sso-client2
  
  pom

  
    
      
        io.spring.platform
        platform-bom
        Brussels-SR4
        pom
        import
      
      
        org.springframework.cloud
        spring-cloud-dependencies
        Dalston.SR2
        pom
        import
      
    
  

  
    
      
        org.apache.maven.plugins
        maven-compiler-plugin
        2.3.2
        
          1.8
          1.8
          UTF-8
        
      
    
  

sso-server

<&#63;xml version="1.0" encoding="UTF-8"&#63;>

  
    sso-demo
    study.security.sso
    1.0.0-SNAPSHOT
  
  4.0.0

  sso-server

  
    
      org.springframework.boot
      spring-boot-starter-security
    
    
      org.springframework.boot
      spring-boot-starter-web
    
    
      org.springframework.security.oauth
      spring-security-oauth2
    
    
      org.springframework.security
      spring-security-jwt
    

  

sso-client与sso-client2 pom 中的 是一样的

1.sso-server

现在开始搭建认证服务器

认证服务器的目录结构如下

这里写图片描述

/**
 * 认证服务器配置
 * Created by ZhuPengWei on 2018/1/11.
 */
@Configuration
@EnableAuthorizationServer
public class SsoAuthenticationServerConfig extends AuthorizationServerConfigurerAdapter {

  @Override
  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
        .withClient("client1")
        .secret("client1")
        .authorizedGrantTypes("authorization_code", "refresh_token")
        .scopes("all")
        .and()
        .withClient("client2")
        .secret("client2")
        .authorizedGrantTypes("authorization_code", "refresh_token")
        .scopes("all");
  }

  @Override
  public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter());
  }


  /**
   * 认证服务器的安全配置
   *
   * @param security
   * @throws Exception
   */
  @Override
  public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    // 要访问认证服务器tokenKey的时候需要经过身份认证
    security.tokenKeyAccess("isAuthenticated()");
  }

  @Bean
  public TokenStore jwtTokenStore() {
    return new JwtTokenStore(jwtAccessTokenConverter());
  }

  @Bean
  public JwtAccessTokenConverter jwtAccessTokenConverter() {
    JwtAccessTokenConverter jwtAccessTokenCOnverter= new JwtAccessTokenConverter();
    // 保证JWT安全的唯一方式
    jwtAccessTokenConverter.setSigningKey("ZPW");
    return jwtAccessTokenConverter;
  }
}
/**
 * 自定义用户登陆逻辑配置
 * Created by ZhuPengWei on 2018/1/13.
 */
@Configuration
public class SsoSecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private UserDetailsService userDetailsService;

  /**
   * 加密解密逻辑
   */
  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    // 改成表单登陆的方式 所有请求都需要认证
    http.formLogin().and().authorizeRequests().anyRequest().authenticated();
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    // 用自己的登陆逻辑以及加密器
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
  }
}
/**
 * 自定义用户登陆
 * Created by ZhuPengWei on 2018/1/13.
 */
@Component
public class SsoUserDetailsService implements UserDetailsService {

  @Autowired
  private PasswordEncoder passwordEncoder;

  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

    return new User(username,
        passwordEncoder.encode("123456"),
        AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
  }
}

其中SsoApprovalEndPoint与SsoSpelView目的是去掉登陆之后授权的效果

注解 @FrameworkEndpoint

与@RestController注解相类似

如果声明和@FrameworkEndpoint一模一样的@RequestMapping

Spring框架处理的时候会优先处理@RestController里面的

这里写图片描述

/**
 * 自定义认证逻辑
 * Created by ZhuPengWei on 2018/1/13.
 */
@RestController
@SessionAttributes("authorizationRequest")
public class SsoApprovalEndpoint {

  @RequestMapping("/oauth/confirm_access")
  public ModelAndView getAccessConfirmation(Map model, HttpServletRequest request) throws Exception {
    String template = createTemplate(model, request);
    if (request.getAttribute("_csrf") != null) {
      model.put("_csrf", request.getAttribute("_csrf"));
    }
    return new ModelAndView(new SsoSpelView(template), model);
  }

  protected String createTemplate(Map model, HttpServletRequest request) {
    String template = TEMPLATE;
    if (model.containsKey("scopes") || request.getAttribute("scopes") != null) {
      template = template.replace("%scopes%", createScopes(model, request)).replace("%denial%", "");
    } else {
      template = template.replace("%scopes%", "").replace("%denial%", DENIAL);
    }
    if (model.containsKey("_csrf") || request.getAttribute("_csrf") != null) {
      template = template.replace("%csrf%", CSRF);
    } else {
      template = template.replace("%csrf%", "");
    }
    return template;
  }

  private CharSequence createScopes(Map model, HttpServletRequest request) {
    StringBuilder builder = new StringBuilder("
    "); @SuppressWarnings("unchecked") Map scopes = (Map) (model.containsKey("scopes") &#63; model.get("scopes") : request .getAttribute("scopes")); for (String scope : scopes.keySet()) { String approved = "true".equals(scopes.get(scope)) &#63; " checked" : ""; String denied = !"true".equals(scopes.get(scope)) &#63; " checked" : ""; String value = SCOPE.replace("%scope%", scope).replace("%key%", scope).replace("%approved%", approved) .replace("%denied%", denied); builder.append(value); } builder.append("
"); return builder.toString(); } private static String CSRF = ""; private static String DENIAL = "%csrf%"; // 对源代码进行处理 隐藏授权页面,并且使他自动提交 private static String TEMPLATE = "

OAuth Approval

" + "

Do you authorize '${authorizationRequest.clientId}' to access your protected resources&#63;

" + "%csrf%%scopes%" + "%denial%
"; private static String SCOPE = "
  • %scope%: Approve Deny
  • "; }

    SsoSpelView 与 原来SpelView 是一样的 只不过原来SpelView 不是public的类

    application.properties

    server.port=9999
    server.context-path=/server

    2.sso-client

    相对于认证服务器 资源服务器demo的配置就十分简单了

    这里写图片描述

    /**
     * Created by ZhuPengWei on 2018/1/11.
     */
    @SpringBootApplication
    @RestController
    @EnableOAuth2Sso
    public class SsoClient1Application {
    
      @GetMapping("/user")
      public Authentication user(Authentication user) {
        return user;
      }
    
      public static void main(String[] args) {
        SpringApplication.run(SsoClient1Application.class, args);
      }
    }
    
    
    
      
      
    
    
    

    SSO Demo Client1

    访问client2

    application.properties

    security.oauth2.client.client-id=client1
    security.oauth2.client.client-secret=client1
    #需要认证时候跳转的地址
    security.oauth2.client.user-authorization-uri=http://127.0.0.1:9999/server/oauth/authorize
    #请求令牌地址
    security.oauth2.client.access-token-uri=http://127.0.0.1:9999/server/oauth/token
    #解析
    security.oauth2.resource.jwt.key-uri=http://127.0.0.1:9999/server/oauth/token_key
    
    
    #sso
    server.port=8080
    server.context-path=/client1

    3.sso-client2

    资源服务器1和资源服务器2的目录结构是一样的,改了相关的参数

    /**
     * Created by ZhuPengWei on 2018/1/11.
     */
    @SpringBootApplication
    @RestController
    @EnableOAuth2Sso
    public class SsoClient2Application {
    
      @GetMapping("/user")
      public Authentication user(Authentication user) {
        return user;
      }
    
      public static void main(String[] args) {
        SpringApplication.run(SsoClient2Application.class, args);
      }
    }
    
    
    
      
      
    
    
    

    SSO Demo Client2

    访问client1
    security.oauth2.client.client-id=client2
    security.oauth2.client.client-secret=client2
    #需要认证时候跳转的地址
    security.oauth2.client.user-authorization-uri=http://127.0.0.1:9999/server/oauth/authorize
    #请求令牌地址
    security.oauth2.client.access-token-uri=http://127.0.0.1:9999/server/oauth/token
    #解析
    security.oauth2.resource.jwt.key-uri=http://127.0.0.1:9999/server/oauth/token_key
    
    
    #sso
    server.port=8060
    server.context-path=/client2

    好了 基于JWT实现SSO单点登录的DEMO以及搭建完成了 下面来看看页面的效果

    在初次访问的时候

    图1

    这里写图片描述

    登陆成功之后

    图2

    这里写图片描述

    图3

    这里写图片描述

    注意

    写SsoApprovalEndPoint与SsoSpelView目的是去掉登陆之后授权的效果如果不写这2个类

    在初次访问的登陆成功之后是有一步授权的操作的

    比如说图1操作成功之后

    这里写图片描述 

    点击Authorize才会跳转到图2

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


    推荐阅读
    • 本文最初发表在Thorben Janssen的Java EE博客上,每周都会分享最新的Java新闻和动态。 ... [详细]
    • 用阿里云的免费 SSL 证书让网站从 HTTP 换成 HTTPS
      HTTP协议是不加密传输数据的,也就是用户跟你的网站之间传递数据有可能在途中被截获,破解传递的真实内容,所以使用不加密的HTTP的网站是不 ... [详细]
    • 网站访问全流程解析
      本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
    • 小程序的授权和登陆
      小程序的授权和登陆 ... [详细]
    • 本文介绍了 Go 语言中的高性能、可扩展、轻量级 Web 框架 Echo。Echo 框架简单易用,仅需几行代码即可启动一个高性能 HTTP 服务。 ... [详细]
    • Cookie学习小结
      Cookie学习小结 ... [详细]
    • HTTP(HyperTextTransferProtocol)是超文本传输协议的缩写,它用于传送www方式的数据。HTTP协议采用了请求响应模型。客服端向服务器发送一 ... [详细]
    • 如何在服务器上配置SSL证书
      SSL证书是用于验证互联网上身份的一种数字凭证,通过启用HTTPS协议,确保用户与服务器之间的通信安全。本文将详细介绍如何在API和服务器上配置SSL证书,以提升网站的安全性和可信度。 ... [详细]
    • javascript分页类支持页码格式
      前端时间因为项目需要,要对一个产品下所有的附属图片进行分页显示,没考虑ajax一张张请求,所以干脆一次性全部把图片out,然 ... [详细]
    • 在PHP中如何正确调用JavaScript变量及定义PHP变量的方法详解 ... [详细]
    • 本文将深入探讨 iOS 中的 Grand Central Dispatch (GCD),并介绍如何利用 GCD 进行高效多线程编程。如果你对线程的基本概念还不熟悉,建议先阅读相关基础资料。 ... [详细]
    • 一个初秋的雨夜,我独自漫步在校园的小道上,心中突然涌起对理想爱情的憧憬。这篇文章将分享我对理想伴侣的期望,以及与他共度美好时光的愿景。 ... [详细]
    • 本文旨在探讨信息安全专业的职业规划路径,结合个人经历和专家建议,为即将毕业的学生提供实用的指导。 ... [详细]
    • 使用ArcGIS for Java和Flex浏览自定义ArcGIS Server 9.3地图
      本文介绍了如何在Flex应用程序中实现浏览自定义ArcGIS Server 9.3发布的地图。这是一个基本的入门示例,适用于初学者。 ... [详细]
    • 探索Web 2.0新概念:Widget
      尽管你可能尚未注意到Widget,但正如几年前对RSS的陌生一样,这一概念正逐渐走入大众视野。据美国某权威杂志预测,2007年将是Widget年。本文将详细介绍Widget的定义、功能及其未来发展趋势。 ... [详细]
    author-avatar
    手机用户2602934117
    这个家伙很懒,什么也没留下!
    PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
    Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有