美文网首页spring bootspringbootOauth2
springcloud-sso单点登陆(一)

springcloud-sso单点登陆(一)

作者: 凌康ACG | 来源:发表于2019-12-23 22:45 被阅读0次

    单点登陆的实现方式有多种:
    1、基于cas来做
    2、spring cloud oauth2的spring全家桶
    3、自定义jwt(不推荐)
    下面我们基于spring全家桶来做一个单点登陆系统2019年12月23日最新springboot版本2.2.2.RELEASE;由于篇幅问题,我们分成两篇文章。
    下一篇:https://www.jianshu.com/p/074653aaa405
    源码:https://github.com/xcocean/spring-cloud-sso

    一、搭建统一依赖管理

    项目maven父子项目结构:
    parent(root)
          |__dependencies
          |__oauth2-server
          |__business-user

    1、创建一个文件夹spring-cloud-sso用idea打开,然后new pom.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.2.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.qbccn</groupId>
        <artifactId>parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>pom</packaging>
        <url>http://www.qbccn.com</url>
        <modules>
            <module>dependencies</module>
        </modules>
        <properties>
            <java.version>1.8</java.version>
            <maven.compiler.source>${java.version}</maven.compiler.source>
            <maven.compiler.target>${java.version}</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        </properties>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>com.qbccn</groupId>
                    <artifactId>dependencies</artifactId>
                    <version>${project.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <profiles>
            <profile>
                <id>default</id>
                <activation>
                    <activeByDefault>true</activeByDefault>
                </activation>
                <properties>
                    <spring-javaformat.version>0.0.12</spring-javaformat.version>
                </properties>
                <build>
                    <plugins>
                        <plugin>
                            <groupId>io.spring.javaformat</groupId>
                            <artifactId>spring-javaformat-maven-plugin</artifactId>
                            <version>${spring-javaformat.version}</version>
                        </plugin>
                        <plugin>
                            <groupId>org.apache.maven.plugins</groupId>
                            <artifactId>maven-surefire-plugin</artifactId>
                            <configuration>
                                <includes>
                                    <include>**/*Tests.java</include>
                                </includes>
                                <excludes>
                                    <exclude>**/Abstract*.java</exclude>
                                </excludes>
                                <systemPropertyVariables>
                                    <java.security.egd>file:/dev/./urandom</java.security.egd>
                                    <java.awt.headless>true</java.awt.headless>
                                </systemPropertyVariables>
                            </configuration>
                        </plugin>
                        <plugin>
                            <groupId>org.apache.maven.plugins</groupId>
                            <artifactId>maven-enforcer-plugin</artifactId>
                            <executions>
                                <execution>
                                    <id>enforce-rules</id>
                                    <goals>
                                        <goal>enforce</goal>
                                    </goals>
                                    <configuration>
                                        <rules>
                                            <bannedDependencies>
                                                <excludes>
                                                    <exclude>commons-logging:*:*</exclude>
                                                </excludes>
                                                <searchTransitive>true</searchTransitive>
                                            </bannedDependencies>
                                        </rules>
                                        <fail>true</fail>
                                    </configuration>
                                </execution>
                            </executions>
                        </plugin>
                        <plugin>
                            <groupId>org.apache.maven.plugins</groupId>
                            <artifactId>maven-install-plugin</artifactId>
                            <configuration>
                                <skip>true</skip>
                            </configuration>
                        </plugin>
                        <plugin>
                            <groupId>org.apache.maven.plugins</groupId>
                            <artifactId>maven-javadoc-plugin</artifactId>
                            <configuration>
                                <skip>true</skip>
                            </configuration>
                            <inherited>true</inherited>
                        </plugin>
                    </plugins>
                </build>
            </profile>
        </profiles>
        <repositories>
            <repository>
                <id>alimaven</id>
                <name>aliyun maven</name>
                <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
            <repository>
                <id>spring-milestone</id>
                <name>Spring Milestone</name>
                <url>https://repo.spring.io/milestone</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
            <repository>
                <id>spring-snapshot</id>
                <name>Spring Snapshot</name>
                <url>https://repo.spring.io/snapshot</url>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
            </repository>
        </repositories>
        <pluginRepositories>
            <pluginRepository>
                <id>spring-milestone</id>
                <name>Spring Milestone</name>
                <url>https://repo.spring.io/milestone</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </pluginRepository>
            <pluginRepository>
                <id>spring-snapshot</id>
                <name>Spring Snapshot</name>
                <url>https://repo.spring.io/snapshot</url>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
            </pluginRepository>
        </pluginRepositories>
    </project>
    

    然后在项目下创建一个文件夹dependencies,在dependencies下创建pom.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.qbccn</groupId>
        <artifactId>dependencies</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>pom</packaging>
        <url>http://www.qbccn.com</url>
    
        <properties>
            <!--spring cloud-->
            <spring-cloud-starter-oauth2>2.1.4.RELEASE</spring-cloud-starter-oauth2>
    
            <!--mysql-->
            <HikariCP>3.2.0</HikariCP>
            <mybatis-spring-boot-starter>2.1.1</mybatis-spring-boot-starter>
            <mysql-connector-java>8.0.18</mysql-connector-java>
    
            <!--http-->
            <okhttp>3.14.2</okhttp>
    
            <!--other-->
            <lombok>1.18.10</lombok>
        </properties>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                    <version>${lombok}</version>
                    <optional>true</optional>
                </dependency>
    
                <!-- oauth2 server  start-->
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-oauth2</artifactId>
                    <version>${spring-cloud-starter-oauth2}</version>
                </dependency>
                <!-- oauth2 server  end-->
    
                <dependency>
                    <groupId>org.mybatis.spring.boot</groupId>
                    <artifactId>mybatis-spring-boot-starter</artifactId>
                    <version>${mybatis-spring-boot-starter}</version>
                </dependency>
    
                <dependency>
                    <groupId>com.zaxxer</groupId>
                    <artifactId>HikariCP</artifactId>
                    <version>${HikariCP}</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-jdbc</artifactId>
                    <exclusions>
                        <!-- 排除 tomcat-jdbc 以使用 HikariCP -->
                        <exclusion>
                            <groupId>org.apache.tomcat</groupId>
                            <artifactId>tomcat-jdbc</artifactId>
                        </exclusion>
                    </exclusions>
                </dependency>
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>${mysql-connector-java}</version>
                </dependency>
    
                <dependency>
                    <groupId>com.squareup.okhttp3</groupId>
                    <artifactId>okhttp</artifactId>
                    <version>${okhttp}</version>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <repositories>
            <repository>
                <id>alimaven</id>
                <name>aliyun maven</name>
                <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
            <repository>
                <id>spring-milestone</id>
                <name>Spring Milestone</name>
                <url>https://repo.spring.io/milestone</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
            <repository>
                <id>spring-snapshot</id>
                <name>Spring Snapshot</name>
                <url>https://repo.spring.io/snapshot</url>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
            </repository>
        </repositories>
        <pluginRepositories>
            <pluginRepository>
                <id>spring-milestone</id>
                <name>Spring Milestone</name>
                <url>https://repo.spring.io/milestone</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </pluginRepository>
            <pluginRepository>
                <id>spring-snapshot</id>
                <name>Spring Snapshot</name>
                <url>https://repo.spring.io/snapshot</url>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
            </pluginRepository>
        </pluginRepositories>
    </project>
    

    二、创建认证授权SSO:oauth2-server

    spring-cloud-sso下创建文件夹oauth2-server,在oauth2-server下创建pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>com.qbccn</groupId>
            <artifactId>parent</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
    
        <groupId>com.qbccn</groupId>
        <artifactId>oauth2-server</artifactId>
        <description>认证授权中心</description>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
            <!-- oauth2 server  start-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-oauth2</artifactId>
            </dependency>
            <!-- oauth2 server  end-->
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>com.zaxxer</groupId>
                <artifactId>HikariCP</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
                <exclusions>
                    <!-- 排除 tomcat-jdbc 以使用 HikariCP -->
                    <exclusion>
                        <groupId>org.apache.tomcat</groupId>
                        <artifactId>tomcat-jdbc</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.squareup.okhttp3</groupId>
                <artifactId>okhttp</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <mainClass>com.qbccn.Oauth2ServerApplication</mainClass>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    在父pom.xml加上<module>oauth2-server</module>
    然后关闭IDEA,重新打开让idea识别maven项目,更新项目。
    然后手动创建src/main/javasrc/main/resources;在java下创建com.qbccn.Oauth2ServerApplication.java基本的springboot项目:

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

    关键配置如下
    1、WebSecurityConfiguration

    package com.qbccn.config;
    
    import com.qbccn.config.impl.UserDetailsServiceImpl;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
    public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @Bean
        public BCryptPasswordEncoder passwordEncoder() {
            // 设置默认的加密方式
            return new BCryptPasswordEncoder();
        }
    
        /**
         * 用于支持 password 模式
         */
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    
        @Bean
        @Override
        public UserDetailsService userDetailsService() {
            return new UserDetailsServiceImpl();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            // 使用自定义认证与授权
            auth.userDetailsService(userDetailsService());
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //关闭跨域认证
            http.csrf().disable();
            http.exceptionHandling()
                    .and()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }
    }
    

    2、AuthorizationServerConfiguration

    package com.qbccn.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
    import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
    import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
    import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
    import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
    import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
    
    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    
        @Autowired
        private RedisConnectionFactory redisConnectionFactory;
    
        @Autowired
        private BCryptPasswordEncoder passwordEncoder;
    
        /**
         * 注入用于支持 password 模式
         */
        @Autowired
        private AuthenticationManager authenticationManager;
    
        @Bean
        public TokenStore tokenStore() {
            // 基于 redis 实现,令牌保存到redis,也可以存到数据库中
            return new RedisTokenStore(redisConnectionFactory);
        }
    
        @Bean
        @Primary
        public DefaultTokenServices defaultTokenServices() {
            DefaultTokenServices services = new DefaultTokenServices();
            services.setAccessTokenValiditySeconds(60 * 60 * 12);//设置token 20秒过期
            services.setRefreshTokenValiditySeconds(60 * 60 * 24 * 30);//设置刷新token的过期时间
            services.setTokenStore(tokenStore());
            return services;
        }
    
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints
                    // 用于支持密码模式
                    .authenticationManager(authenticationManager)
                    .tokenStore(tokenStore());
        }
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security
                    // 允许客户端访问 /oauth/check_token 检查 token
                    .checkTokenAccess("isAuthenticated()")
                    .allowFormAuthenticationForClients();
        }
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            // 配置客户端,一般是读取数据库的,因为可能会变动,这里我们写死放在内存中即可
            clients
                    // 使用内存设置
                    .inMemory()
                    // client_id
                    .withClient("client")
                    // client_secret
                    .secret(passwordEncoder.encode("secret"))
                    // 授权类型
                    .authorizedGrantTypes("password", "refresh_token")
                    //资源Id
                    .resourceIds("backend-resources")
                    // 授权范围
                    .scopes("backend");
        }
    }
    

    3、UserDetailsServiceImpl

    package com.qbccn.config.impl;
    
    import com.qbccn.entity.SsoRole;
    import com.qbccn.entity.SsoUser;
    import com.qbccn.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.stereotype.Component;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Component
    public class UserDetailsServiceImpl implements UserDetailsService {
    
        @Autowired
        private UserService userService;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            // 查询用户信息
            SsoUser user = userService.getUserInfo(username);
            if (user == null) {
                throw new UsernameNotFoundException("帐号:" + username + " 不存在!");
            }
            List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
            //查询拥有的角色
            List<SsoRole> roles = userService.getRoleByUserId(user.getId());
            roles.forEach(r -> {
                //security的验证规则需要ROLE_ 对应数据库的角色名称也要以ROLE_XXXX这种形式
                grantedAuthorities.add(new SimpleGrantedAuthority(r.getRoleName()));
            });
            //spring5+要求密码加密,这里传明文密码,所以再加密
            String password = new BCryptPasswordEncoder().encode(user.getPassword());
            return new User(user.getUsername(), password, grantedAuthorities);
        }
    }
    

    application.yml:

    spring:
      main:
        allow-bean-definition-overriding: true
      datasource:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/sso?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
        username: root
        password: 123456
        hikari:
          minimum-idle: 1
          idle-timeout: 600000
          maximum-pool-size: 10
          auto-commit: true
          pool-name: MyHikariCP
          max-lifetime: 1800000
          connection-timeout: 30000
          connection-test-query: SELECT 1
    
      redis:
        port: 6379
        host: 127.0.0.1
        jedis:
          pool:
            min-idle: 1
    
    mybatis:
      mapper-location: classpath:mapper/*.xml
    

    SQL:

    DROP TABLE IF EXISTS `sso_role`;
    CREATE TABLE `sso_role` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `user_id` int(11) DEFAULT NULL,
      `role_name` varchar(30) DEFAULT NULL,
      `role_desc` varchar(50) DEFAULT NULL,
      `create_time` datetime DEFAULT NULL,
      `update_time` datetime DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
    
    INSERT INTO `sso_role` VALUES ('1', '1', 'ROLE_USER', '用户角色', '2019-12-23 22:28:45', null);
    INSERT INTO `sso_role` VALUES ('2', '2', 'ROLE_USER', '用户角色', '2019-12-23 22:28:45', null);
    INSERT INTO `sso_role` VALUES ('3', '2', 'ROLE_ADMIN', '管理员', '2019-12-23 22:28:45', null);
    
    DROP TABLE IF EXISTS `sso_user`;
    CREATE TABLE `sso_user` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(20) DEFAULT NULL,
      `username` varchar(20) DEFAULT NULL,
      `password` varchar(20) DEFAULT NULL,
      `create_time` datetime DEFAULT NULL,
      `update_time` datetime DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
    
    INSERT INTO `sso_user` VALUES ('1', '凌康', 'lk', '123', '2019-12-23 22:28:11', null);
    INSERT INTO `sso_user` VALUES ('2', 'admin', 'admin', '123', '2019-12-23 22:28:23', null);
    

    运行项目用postman测试:


    image.png

    项目结构如下


    image.png

    相关文章

      网友评论

        本文标题:springcloud-sso单点登陆(一)

        本文链接:https://www.haomeiwen.com/subject/kulpnctx.html