• 基于JWT实现SSO单点登录


      一、基于JWT实现SSO单点登录原理

      1、什么是单点登录

      所谓单点登录就是有多个应用部署在不同的服务器上,只需登录一次就可以互相访问不同服务器上的资源。

      2、单点登录流程

      

      当一个访问请求发给应用A,如果这个请求需要登录以后才能访问,那么应用A就会向认证服务器请求授权,这时候就把用户引导到认证服务器上。用户在认证服务器上完成认证并授权。认证授权完成后,认证服务器返回给应用A一个授权码,应用A携带授权码到认证服务器请求令牌,认证服务器返回应用A一个JWT,应用A解析JWT里面的信息,完成登录。这是一个标准的OAuth2的授权码流程。

      走完认证流程后,给出去的JWT实际上里面包含的就是当前用户在认证服务器上登录以后用户的认证信息,应用A解析JWT后,自己生成一个经过认证的Authentication放到它的SpringSecurity和SecurityContext里面。

      当访问应用服务器B的时候,同样引导用户去认证服务器请求授权(不需要登录),用户授权可以用登录的信息去访问应用B,后面同样是授权码流程,返回JWT给应用B。两个应用返回不同的JWT,但是解析出的信息是一样的。 

      二、实现单点登录

      1、父工程(sso-demo)

      1)pom.xml

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.0.4.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.security.oauth.boot</groupId>
                <artifactId>spring-security-oauth2-autoconfigure</artifactId>
                <version>2.1.3.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-jwt</artifactId>
                <version>1.0.10.RELEASE</version>
            </dependency>
        </dependencies>
    </dependencyManagement>                

      2、认证服务(sso-server)

      1)pom.xml

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security.oauth.boot</groupId>
        <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-jwt</artifactId>
    </dependency>

      2)application.properties

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

      3)WebSecurityConfig.java

    @EnableWebSecurity
    @Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
        
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.httpBasic().and().csrf().disable();     
        }
       @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }

      4)MyUserDetailsService.java

    @Component
    public class MyUserDetailsService implements UserDetailsService{
    
        @Autowired
        private PasswordEncoder passwordEncoder;
        
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            System.out.println("登录用户名:"+username);
            String  password = passwordEncoder.encode("123456");
            return new User(username,password,true,true,true,true,
                    AuthorityUtils.commaSeparatedStringToAuthorityList("all"));
        }
    }

      5)SsoAuthorizationServerConfig.java

    @Configuration
    @EnableAuthorizationServer
    public class SsoAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
        
        @Autowired
        private PasswordEncoder passwordEncoder;
        
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                   .withClient("appclient_1").secret(passwordEncoder.encode("client1_123456"))
                   .authorizedGrantTypes("authorization_code","refresh_token")
                   .scopes("all")
                   .redirectUris("http://127.0.0.1:8080/client1/login")
                 .and()
                   .withClient("appclient_2").secret(passwordEncoder.encode("client2_123456"))
                   .authorizedGrantTypes("authorization_code","refresh_token")
                   .scopes("all")
                   .redirectUris("http://127.0.0.1:8060/client2/login");
        }
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter());
        }
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security.tokenKeyAccess("isAuthenticated()");//访问tokenKey(秘钥shxiang)的时候需要身份认证 
        }
        @Bean
        public TokenStore jwtTokenStore() {
            return new JwtTokenStore(jwtAccessTokenConverter());
        }
        @Bean
        public JwtAccessTokenConverter jwtAccessTokenConverter() {
            JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
            accessTokenConverter.setSigningKey("shxiang");//设置秘钥
            return accessTokenConverter;
        }
    }

      6)SsoServerApplication.java

    @SpringBootApplication
    public class SsoServerApplication {
        public static void main(String[] args) {
            SpringApplication.run(SsoServerApplication.class, args);
        }
    }

      3、应用1(sso-client1)

      1)pom.xml,同上

      2)application.properties

    security.oauth2.client.client-id = appclient_1
    security.oauth2.client.client-secret = client1_123456
    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
    
    server.port=8080
    server.servlet.context-path =/client1

      3)index.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>SSO Client1</title>
    </head>
    <body>
        <h1>SSO Demo Client1</h1>
        <a href="http://127.0.0.1:8060/client2/index.html">访问Client2</a>
    </body>
    </html>

      4)SsoClient1Application.java

    @SpringBootApplication
    @RestController
    @EnableOAuth2Sso
    public class SsoClient1Application {
        public static void main(String[] args) {
            SpringApplication.run(SsoClient1Application.class, args);
        }
        @GetMapping("/user")
        public Authentication user(Authentication user) {
            return user;
        }
    }

      4、应用2(sso-client2)

      1)pom.xml,同上

      2)application.properties,类比应用1修改

      3)index.html,类比应用1修改

      4)SsoClient2Application.java,同上

      5、测试

      1)浏览器输入:127.0.0.1:8080/client1/index.html

      

      2)用户名随便输入,密码输入123456

      

      3)点击Authorize

      

      4)点击超级链接访问Client2

      

      5)点击Authorize

      

      认证成功,后面点击两个超级链接可以任意访问,无需登录 、无需点击Authorize。

      注意:

      1)虽是同一用户,但是访问http://127.0.0.1:8080/client1/user 和 http://127.0.0.1:8060/client2/user获取的Token值不一样。

      2)实现跳过授权,登录后直接访问,修改如下代码:

      

      3)表单登录与httpBasic登录,修改WebSecurityConfig.java中configure方法

    httpBasic登录:http.httpBasic().and().csrf().disable();     
    表单登录:http.formLogin().and().authorizeRequests().anyRequest().authenticated();

      4)重点:浏览器访问要用127.0.0.1不要用localhost。要设置应用路径server.servlet.context-path =/xxxx,不能直接到端口号。

      

  • 相关阅读:
    Axis,axis2,Xfire以及cxf对比 (转)
    客户端调用服务器端三种方式
    webservice发送数据,取数据的方式
    强制关闭myeclipse出现的问题
    取当前系统的上月时间
    oracle 报Ora-01008错误:oracle 并非所有变量都已绑定的原因.TO_number();动态执行select..into..语句时
    ResultSetMetaData rsmd = rs.getMetaData()是什么意思?
    概率质量函数:怀孕周期的PMF
    Python 正则表达式
    TF-IDF
  • 原文地址:https://www.cnblogs.com/javasl/p/13125669.html
Copyright © 2020-2023  润新知