zoukankan      html  css  js  c++  java
  • security和oauth2.0的整合

    security和oauth2.0的整合

    之前已经介绍过security的相关的介绍,现在所需要做的就是security和oauth2.0的整合,在原有的基础上我们加上一些相关的代码;代码实现如下:

    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>urity.demo</groupId>
        <artifactId>security-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
    
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.10.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
            <!--以下两项需要如果不配置,解析themleaft 会有问题-->
            <thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
            <thymeleaf-layout-dialect.version>2.0.5</thymeleaf-layout-dialect.version>
        </properties>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Dalston.SR5</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
    
                <dependency>
                    <groupId>io.spring.platform</groupId>
                    <artifactId>platform-bom</artifactId>
                    <version>Brussels-SR9</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <!--mybatis与mysql-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.2.0</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <!--druid依赖-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.0.25</version>
            </dependency>
            <!--redis依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    
            <!--jasypt加解密-->
            <dependency>
                <groupId>com.github.ulisesbocchio</groupId>
                <artifactId>jasypt-spring-boot-starter</artifactId>
                <version>1.14</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-security</artifactId>
            </dependency>
    
            <!--oauth2.0-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-oauth2</artifactId>
            </dependency>
    
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>0.7.0</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-jwt</artifactId>
            </dependency>
    
            <!--feign-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-feign</artifactId>
            </dependency>
    
            <!--session集群管理-->
            <dependency>
                <groupId>org.springframework.session</groupId>
                <artifactId>spring-session</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.session</groupId>
                <artifactId>spring-session-data-redis</artifactId>
            </dependency>
    
            <!--zipkin-->
           <!-- <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-sleuth</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-sleuth-zipkin</artifactId>
            </dependency>-->
    
            <!--eureka-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-eureka</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
    
    
            <!--添加static和templates的依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
    
    
            <!--config-->
            <!--<dependency>-->
            <!--<groupId>org.springframework.cloud</groupId>-->
            <!--<artifactId>spring-cloud-starter-config</artifactId>-->
            <!--</dependency>-->
            <!--<dependency>-->
            <!--<groupId>org.springframework.cloud</groupId>-->
            <!--<artifactId>spring-cloud-starter-bus-amqp</artifactId>-->
            <!--</dependency>-->
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    
    </project>
    
    

    这里我们需要注意导入依赖的版本,版本过高可能会存在一些未知的问题:

    AuthorizationServerConfiguration核心类:

    package urity.demo.oauth2;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.autoconfigure.security.SecurityProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
    import org.springframework.security.oauth2.common.OAuth2AccessToken;
    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.OAuth2Authentication;
    import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
    import urity.demo.service.RedisAuthenticationCodeServices;
    
    import javax.annotation.Resource;
    import java.util.HashMap;
    import java.util.Map;
    
    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    
        @Value("${resource.id:spring-boot-application}")
        private String resourceId;
    
        @Value("${access_token.validity_period:36000}")
        private int accessTokenValiditySeconds = 36000;
    
        //认证管理 很重要 如果security版本高可能会出坑哦
        @Resource
        private AuthenticationManager authenticationManager;
    
        @Resource
        private RedisAuthenticationCodeServices redisAuthenticationCodeServices;
    
    
        //定义令牌端点上的安全约束。
        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
            oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')");
            oauthServer.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
    
        }
    
        //将ClientDetailsServiceConfigurer(从您的回调AuthorizationServerConfigurer)可以用来在内存或JDBC实现客户的细节服务来定义的。客户端的重要属性是
        //clientId:(必填)客户端ID。
        //secret:(可信客户端需要)客户机密码(如果有)。
        //scope:客户受限的范围。如果范围未定义或为空(默认值),客户端不受范围限制。
        //authorizedGrantTypes:授予客户端使用授权的类型。默认值为空。
        //authorities授予客户的授权机构(普通的Spring Security权威机构)。
        //客户端的详细信息可以通过直接访问底层商店(例如,在数据库表中JdbcClientDetailsService)或通过ClientDetailsManager接口(这两种实现ClientDetailsService也实现)来更新运行的应用程序。
        //注意:JDBC服务的架构未与库一起打包(因为在实践中可能需要使用太多变体)
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    
            //默认值InMemoryTokenStore对于单个服务器是完全正常的(即,在发生故障的情况下,低流量和热备份备份服务器)。大多数项目可以从这里开始,也可以在开发模式下运行,以便轻松启动没有依赖关系的服务器。
            //这JdbcTokenStore是同一件事的JDBC版本,它将令牌数据存储在关系数据库中。如果您可以在服务器之间共享数据库,则可以使用JDBC版本,如果只有一个,则扩展同一服务器的实例,或者如果有多个组件,则授权和资源服务器。要使用JdbcTokenStore你需要“spring-jdbc”的类路径。
    
    
            clients.inMemory()
                    //client Id
                    .withClient("normal-app")
                    .authorizedGrantTypes("authorization_code", "implicit")
                    .authorities("ROLE_CLIENT")
                    .scopes("read","write")
                    .resourceIds(resourceId)
                    .accessTokenValiditySeconds(accessTokenValiditySeconds)
                    .and()
                    .withClient("trusted-app")
                    .authorizedGrantTypes("client_credentials", "password")
                    .authorities("ROLE_TRUSTED_CLIENT")
                    .scopes("read", "write")
                    .resourceIds(resourceId)
                    .accessTokenValiditySeconds(accessTokenValiditySeconds)
                    .secret("secret");
    
    
    
    
        }
    
        //AuthorizationEndpoint可以通过以下方式配置支持的授权类型AuthorizationServerEndpointsConfigurer。默认情况下,所有授权类型均受支持,除了密码(有关如何切换它的详细信息,请参见下文)。以下属性会影响授权类型:
        //authenticationManager:通过注入密码授权被打开AuthenticationManager。
        //userDetailsService:如果您注入UserDetailsService或者全局配置(例如a GlobalAuthenticationManagerConfigurer),则刷新令牌授权将包含对用户详细信息的检查,以确保该帐户仍然活动
        //authorizationCodeServices:定义AuthorizationCodeServices授权代码授权的授权代码服务(实例)。
        //implicitGrantService:在批准期间管理状态。
        //tokenGranter:(TokenGranter完全控制授予和忽略上述其他属性)
        //在XML授予类型中包含作为子元素authorization-server。
    
        /**
         * /oauth/authorize您可以从该请求中获取所有数据,
         * 然后根据需要进行渲染,
         * 然后所有用户需要执行的操作都是回复有关批准或拒绝授权的信息。
         * 请求参数直接传递给您UserApprovalHandler,
         * AuthorizationEndpoint所以您可以随便解释数据
         *
         * @param endpoints
         * @throws Exception
         */
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(this.authenticationManager);
            endpoints.accessTokenConverter(accessTokenConverter());//jwt
            endpoints.tokenStore(tokenStore());
    
            //授权码存储
            endpoints.authorizationCodeServices(redisAuthenticationCodeServices);
    
        }
    
        @Bean
        public JwtAccessTokenConverter accessTokenConverter() {
    
            JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter() {
                /**
                 * 重写增强token的方法
                 * 自定义返回相应的信息
                 *
                 */
    
                @Override
                public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
    
                    String userName = authentication.getUserAuthentication().getName();
                    // 与登录时候放进去的UserDetail实现类一直查看link{SecurityConfiguration}
                    User user = (User) authentication.getUserAuthentication().getPrincipal();
                    /** 自定义一些token属性 ***/
                    final Map<String, Object> additionalInformation = new HashMap<>();
                    additionalInformation.put("userName", userName);
                    additionalInformation.put("roles", user.getAuthorities());
                    ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
                    OAuth2AccessToken enhancedToken = super.enhance(accessToken, authentication);
                    return enhancedToken;
                }
    
            };
            // 测试用,资源服务使用相同的字符达到一个对称加密的效果,生产时候使用RSA非对称加密方式
            accessTokenConverter.setSigningKey("123");
            return accessTokenConverter;
    
        }
    
        @Bean
        public TokenStore tokenStore() {
    
            TokenStore tokenStore = new JwtTokenStore(accessTokenConverter());
    
            return tokenStore;
        }
    
    }
    
    

    RedisAuthenticationCodeServices:

    我们把授权码存在了redis中:

    package urity.demo.service;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.data.redis.connection.RedisConnection;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.security.oauth2.provider.OAuth2Authentication;
    import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices;
    import org.springframework.stereotype.Service;
    import org.springframework.util.Assert;
    import org.springframework.security.oauth2.common.util.SerializationUtils;
    
    
    //自定义为使用redis存储授权码
    @Service
    @Slf4j
    public class RedisAuthenticationCodeServices extends RandomValueAuthorizationCodeServices {
    
        private static final String AUTH_CODE_KEY = "my_code";
    
        private RedisConnectionFactory connectionFactory;
    
        public RedisAuthenticationCodeServices(RedisConnectionFactory connectionFactory) {
            Assert.notNull(connectionFactory, "RedisConnectionFactory required");
            this.connectionFactory = connectionFactory;
        }
    
        private RedisConnection getConnection() {
    
            return connectionFactory.getConnection();
        }
    
        //redis存储
        @Override
        protected void store(String code, OAuth2Authentication authentication) {
    
            RedisConnection conn = getConnection();
    
            try {
    
    
                conn.hSet(AUTH_CODE_KEY.getBytes("utf-8"), code.getBytes("utf-8"),
    
                        SerializationUtils.serialize(authentication)
                );
            } catch (Exception e) {
    
                conn.close();
            }
    
        }
    
        @Override
        protected OAuth2Authentication remove(String code) {
            RedisConnection conn = getConnection();
            try {
    
    
                OAuth2Authentication authentication = null;
    
                try {
                    authentication = SerializationUtils
                            .deserialize(conn.hGet(AUTH_CODE_KEY.getBytes("utf-8"),
                                    code.getBytes("utf-8")));
    
    
                } catch (Exception e) {
                    e.printStackTrace();
    
    
                }
    
    
                if (authentication != null) {
                    conn.hDel(AUTH_CODE_KEY.getBytes("utf-8"),
                            code.getBytes("utf-8"));
                }
    
                return authentication;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                conn.close();
            }
            return null;
        }
    }
    

    ResourceController:

    package urity.demo.controller;
    
    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    /***
     * 受保护的资源服务
     * @author leftso
     *
     */
    @RestController
    @RequestMapping("/resources")
    public class ResourceController {
    	/**
    	 * 需要用户角色权限
    	 * @return
    	 */
        @PreAuthorize("hasRole('ROLE_USER')")
        @RequestMapping(value="user", method=RequestMethod.GET)
        public String helloUser() {
            return "hello user";
        }
        /**
         * 需要管理角色权限
         * 
         * @return
         */
        @PreAuthorize("hasRole('ROLE_ADMIN')")
        @RequestMapping(value="admin", method=RequestMethod.GET)
        public String helloAdmin() {
            return "hello admin";
        }
        /**
         * 需要客户端权限
         * 
         * @return
         */
        @PreAuthorize("hasRole('ROLE_CLIENT')")
        @RequestMapping(value="client", method=RequestMethod.GET)
        public String helloClient() {
            return "hello user authenticated by normal client";
        }
        /**
         * 需要受信任的客户端权限
         * 
         * @return
         */
        @PreAuthorize("hasRole('ROLE_TRUSTED_CLIENT')")
    	@RequestMapping(value="trusted_client", method=RequestMethod.GET)
        public String helloTrustedClient() {
            return "hello user authenticated by trusted client";
        }
    
    	@RequestMapping(value="principal", method=RequestMethod.GET)
        public Object getPrincipal() {
            Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            return principal;
        }
    
    	@RequestMapping(value="roles", method=RequestMethod.GET)
        public Object getRoles() {
            return SecurityContextHolder.getContext().getAuthentication().getAuthorities();
        }
    
    }
    
    

    application.xml:

    server:
      port: 8787
    spring:
      redis:
        host: 127.0.0.1
        port: 6379
    #    password: redis
        database: 0
      datasource:
          url: jdbc:mysql://localhost:3306/test
          username: ***
          password: ***
          driver-class-name: com.mysql.jdbc.Driver
          type: com.alibaba.druid.pool.DruidDataSource
          initialSize: 5
          minIdle: 5
          maxActive: 30
          maxWait: 10000
          timeBetweenEvictionRunsMillis: 60000
          minEvictableIdleTimeMills: 300000
      session:
        store-type: none
    other:
      security:
        oauth2:
          signKey: oauth
    
    
  • 相关阅读:
    elastic-job-lite-console运维平台的部署使用
    elastic-job简单入门
    23种设计模式学习之单例模式
    23种设计模式学习之抽象工厂模式
    23种设计模式学习之静态工厂方法模式
    微信公众号开发之-回调的所有类型
    nginx学习-超详细nginx配置文件
    nginx学习-简介
    23种设计模式学习之一
    linux 下jenkins安装
  • 原文地址:https://www.cnblogs.com/charlypage/p/9383415.html
Copyright © 2011-2022 走看看