springcloud授权服务器

授权服务器搭建

一. 准备依赖

<dependencies>
    <!-- nacos 注册发现-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
	<!-- oauth2 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
	<!-- web starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
</dependencies>

1. 启动类

package com.chaoyang;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

2. 配置(application.yml)

server:
  port: 12000
spring:
  application:
    name: authorization-server
  cloud:
    nacos:
      discovery:
        server-addr: www.nacos-server.com:8848
  redis:
    host: www.redis-server.com
    port: 6379
  datasource:
    url: jdbc:mysql://www.db.com:3306/coin-exchange?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
    username: root
    password: 123
    driver-class-name: com.mysql.jdbc.Driver

3. 新建配置类(简易版)

/**
* 授权服务配置类
*/
package com.chaoyang.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
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;

@Configuration  // 配置类注解
@EnableAuthorizationServer // 开启授权服务
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
	/**
	* 密码加密器
	*/
    @Autowired
    private PasswordEncoder passwordEncoder;
	
    /**
    * 授权(验证)管理器
    */
    @Autowired
    private AuthenticationManager authenticationManager;
	
    /**
    * 客户信息服务
    */
    @Autowired
    private UserDetailsService userDetailsService;


    /**
     * 添加第三方客户端
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 使用内存方式
        clients.inMemory()
                .withClient("coin-api")// 第三方客户端名称
                .secret(passwordEncoder.encode("123")) // 第三方客户端密码
                .scopes("all")// 授权范围
                .accessTokenValiditySeconds(3600)// token有效期
                .refreshTokenValiditySeconds(7200*7); // refresh_token有效期
        super.configure(clients);
    }


    /**
     * 配置验证管理器
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(this.authenticationManager).userDetailsService(userDetailsService);
        super.configure(endpoints);
    }
}

/*
* WebsecurityConfig 类
*/
package com.chaoyang.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

import java.util.Arrays;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	/**
	* 禁用csrf 
	*/
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.authorizeRequests().anyRequest().authenticated();
    }
    
	/**
	* 使用默认的验证管理器
	*/
    @Bean
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    };
    
	/**
	* 自定义客户信息
	*/
    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager  = new InMemoryUserDetailsManager();
        User user = new User("amdin", "password", Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN")));
        inMemoryUserDetailsManager.createUser(user);
        return inMemoryUserDetailsManager;
    }
	/**
	* 密码管理器
	*/
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}

请求示例:

Redis 存储

在授权服务配置中的验证管理中添加 tokenStore

/**
* 授权服务配置类
*/
package com.chaoyang.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
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;

@Configuration  // 配置类注解
@EnableAuthorizationServer // 开启授权服务
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
	/**
	* 密码加密器
	*/
    @Autowired
    private PasswordEncoder passwordEncoder;
	
    /**
    * 授权(验证)管理器
    */
    @Autowired
    private AuthenticationManager authenticationManager;
	
    /**
    * 客户信息服务
    */
    @Autowired
    private UserDetailsService userDetailsService;
	
    /**
    * redis
    */
    @Autowired
	private RedisConnectionFactory redisConnectionFactory;

    /**
     * 添加第三方客户端
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 使用内存方式
        clients.inMemory()
                .withClient("coin-api")// 第三方客户端名称
                .secret(passwordEncoder.encode("123")) // 第三方客户端密码
                .scopes("all")// 授权范围
                .accessTokenValiditySeconds(3600)// token有效期
                .refreshTokenValiditySeconds(7200*7); // refresh_token有效期
        super.configure(clients);
    }


    /**
     * 配置验证管理器
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(this.authenticationManager).userDetailsService(userDetailsService)
            .tokenStore(tokenStore());
        super.configure(endpoints);
    }
    
    /**
    * tokenStore
    */
    private TokenStore tokenStore(){
    	return new RedisTokenStore(redisConnectionFactory);
	}
}
  /**
  * 获取用户信息
  */
  
  package com.chaoyang.controller;
  
  import org.springframework.security.core.Authentication;
  import org.springframework.security.core.context.SecurityContextHolder;
  import org.springframework.web.bind.annotation.GetMapping;
  import org.springframework.web.bind.annotation.RestController;
  
  import java.security.Principal;
  
  @RestController
  public class UserInfoController {
  
      @GetMapping("/user/info")
      public Principal getUserPrincipal(Principal principal) {
          return principal;
      }
  }
  
  /**
  * 开启资源服务配置
  */
  package com.chaoyang.config;
  
  import org.springframework.context.annotation.Configuration;
  import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
  import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
  
  @EnableResourceServer
  @Configuration
  public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
  }
  

请求示例

JWT 版本

# 生成私钥
keytool -genkeypair -alias coinexchange -keyalg RSA -keypass coinexchange -keystore coinexchange.jks -validity 365 -storepass coinexchange

# 根据私钥生成公钥
keytool -list -rfc --keystore mindflow.jks | openssl x509 -inform pem -pubkey
  1. 授权服务器生成 jwt

    /**
    * 授权服务配置类
    */
    package com.chaoyang.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.password.PasswordEncoder;
    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;
    
    @Configuration  // 配置类注解
    @EnableAuthorizationServer // 开启授权服务
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    	/**
    	* 密码加密器
    	*/
        @Autowired
        private PasswordEncoder passwordEncoder;
    	
        /**
        * 授权(验证)管理器
        */
        @Autowired
        private AuthenticationManager authenticationManager;
    	
        /**
        * 客户信息服务
        */
        @Autowired
        private UserDetailsService userDetailsService;
    	
        /**
        * redis
        */
        @Autowired
    	private RedisConnectionFactory redisConnectionFactory;
    
        /**
         * 添加第三方客户端
         */
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            // 使用内存方式
            clients.inMemory()
                    .withClient("coin-api")// 第三方客户端名称
                    .secret(passwordEncoder.encode("123")) // 第三方客户端密码
                    .scopes("all")// 授权范围
                    .accessTokenValiditySeconds(3600)// token有效期
                    .refreshTokenValiditySeconds(7200*7); // refresh_token有效期
            super.configure(clients);
        }
        /**
        * 验证器
        */
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
           endpoints.authenticationManager(authenticationManager)
                   .userDetailsService(userDetailsService)
                           .tokenStore(tokenStore())
                                   .tokenEnhancer(jwtAccessTokenConverter());
           super.configure(endpoints);
        }
    
        private JwtAccessTokenConverter jwtAccessTokenConverter() {
           JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
           // 加载私钥
           ClassPathResource classPathResource = new ClassPathResource("mindflow.jks");
           KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource, "mindflow".toCharArray());
           jwtAccessTokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("mindflow", "mindflow".toCharArray()));
           return jwtAccessTokenConverter;
        }
    
        private TokenStore tokenStore() {
           return new JwtTokenStore(jwtAccessTokenConverter());
        }
    }
    

    请求示例

    4. 网关实现jwt登出引发的二次登录问题

    package com.chaoyang.filter;
    import com.alibaba.fastjson.JSONObject;
    import com.ctc.wstx.util.StringUtil;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    import java.util.List;
    import java.util.Set;
    
    @Component
    public class JwtCheckFilter implements GlobalFilter, Ordered {
        @Autowired
        private RedisTemplate redisTemplate;
    
        @Value("${no.require.urls:/authorization/oauth/token}")
        private Set<String> whitePathList;
    
        /**
         * 过滤器拦截用户的请求后做什么?
         * @param exchange
         * @param chain
         * @return
         */
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            // 1. 该接口是否需要token才能访问
            if(isRequireToken(exchange)){
                return chain.filter(exchange);
            }
            // 2. 获取Token
            String token = getUserToken(exchange);
    
            // 3. 判断客户的token是否有效?
            if(StringUtils.isEmpty(token)){
                return AuthorizationError(exchange);
            }
            // 4.判断redis中是含有这个key
            if(!redisTemplate.hasKey(token)){
                return AuthorizationError(exchange);
            }
            return chain.filter(exchange);
        }
    
        private String getUserToken(ServerWebExchange exchange) {
           String token =  exchange.getRequest().getHeaders().getFirst("Authorization");
           return token == null ? null : token.replace("bearer ", "");
        }
    
        private Mono<Void> AuthorizationError(ServerWebExchange exchange) {
            ServerHttpResponse response = exchange.getResponse();
            response.getHeaders().set("Content-Type", "application/json");
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("error","No Authorization");
            jsonObject.put("errorMsg","Token is null or Error");
            DataBuffer wrap = response.bufferFactory().wrap(jsonObject.toJSONString().getBytes());
            return response.writeWith(Flux.just(wrap));
        }
    
        private boolean isRequireToken(ServerWebExchange exchange) {
            // 获取路由数据
            String path = exchange.getRequest().getURI().getPath();
            System.out.println(path);
            System.out.println(whitePathList);
    
            // 判断是否在白名单中,在白名单中返回true 否则返回false
            return whitePathList.contains(path);
        }
    
        /**
         * 拦截器的顺序
         * @return
         */
        @Override
        public int getOrder() {
            return 0;
        }
    }
    

5. 新建权限表

-- 系统用户表
CREATE TABLE `sys_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键自增ID',
  `username` varchar(32) NOT NULL COMMENT '用户名',
  `password` varchar(255) DEFAULT NULL COMMENT '密码',
  `fullname` varchar(32) DEFAULT NULL COMMENT '全名',
  `mobile` varchar(20) DEFAULT NULL COMMENT '手机号',
  `email` varchar(255) DEFAULT NULL COMMENT '邮箱',
  `status` tinyint(4) DEFAULT '1' COMMENT '用户状态 1启用 0禁用',
  `create_by` int(11) DEFAULT NULL COMMENT '创建人ID',
  `modify_by` int(11) DEFAULT NULL COMMENT '修改人ID',
  `created` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `last_update_time` datetime DEFAULT NULL COMMENT '最后一次修改的时间',
  `is_deleted` tinyint(4) DEFAULT '0' COMMENT '逻辑删除 0未删除  1已删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

-- 系统角色表
CREATE TABLE `sys_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键自增',
  `name` varchar(32) DEFAULT NULL COMMENT '角色名称',
  `code` varchar(32) DEFAULT NULL COMMENT '角色编码',
  `description` varchar(255) DEFAULT NULL COMMENT '角色描述',
  `modify_by` int(11) DEFAULT NULL COMMENT '修改人名称',
  `status` tinyint(4) DEFAULT '1' COMMENT '角色状态 1有效 0失效',
  `created` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `last_update_time` datetime DEFAULT NULL COMMENT '最后一次更新时间',
  `is_deleted` tinyint(4) DEFAULT '0' COMMENT '逻辑删除 0未删除 1已删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

-- 系统权限表
CREATE TABLE `sys_privilege` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `menu_id` int(11) DEFAULT NULL,
  `name` varchar(32) DEFAULT NULL COMMENT '功能点名称',
  `description` varchar(255) DEFAULT NULL COMMENT '功能描述',
  `url` varchar(1024) DEFAULT NULL,
  `method` varchar(32) DEFAULT NULL COMMENT '请求方式',
  `create_by` int(11) DEFAULT NULL COMMENT '创建人ID',
  `modify_by` int(11) DEFAULT NULL COMMENT '修改人ID',
  `created` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `last_update_time` datetime DEFAULT NULL COMMENT '修改时间',
  `is_deleted` tinyint(4) DEFAULT '0' COMMENT '逻辑删除 0未删除 1已删除',
  `status` tinyint(4) DEFAULT '1' COMMENT '状态: 0失效 1有效',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

-- 角色与权限的多对多的表
CREATE TABLE `sys_role_privilege` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_id` int(11) DEFAULT NULL COMMENT '角色ID',
  `privilege_id` int(11) DEFAULT NULL COMMENT '权限ID',
  `is_deleted` tinyint(4) DEFAULT '0' COMMENT '逻辑删除 0未删除 1已删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

-- 用户和角色的多对多的表
CREATE TABLE `sys_user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_id` int(11) DEFAULT NULL COMMENT '角色ID',
  `user_id` int(11) DEFAULT NULL COMMENT '用户ID',
  `create_by` int(11) DEFAULT NULL COMMENT '创建人ID',
  `modify_by` int(11) DEFAULT NULL COMMENT '修改人ID',
  `created` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `last_update_time` datetime DEFAULT NULL COMMENT '最后一次更新时间',
  `status` tinyint(4) DEFAULT '1' COMMENT '状态: 1有效 0无效',
  `is_deleted` tinyint(4) DEFAULT '0' COMMENT '逻辑删除 0未删除 1已删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

-- 系统菜单表
CREATE TABLE `sys_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `parent_id` int(11) DEFAULT NULL COMMENT '上级菜单ID',
  `parent_key` varchar(32) DEFAULT NULL COMMENT '上级菜单唯一KEY值',
  `type` tinyint(4) DEFAULT '1' COMMENT '类型 1-分类 2-节点',
  `name` varchar(32) DEFAULT NULL COMMENT '名称',
  `description` varchar(255) DEFAULT NULL COMMENT '描述信息',
  `target_url` varchar(255) DEFAULT NULL COMMENT '目标地址',
  `sort` int(11) DEFAULT '0' COMMENT '排序索引',
  `status` tinyint(4) DEFAULT '1' COMMENT '状态 0-无效; 1-有效;',
  `create_by` int(11) DEFAULT NULL COMMENT '创建人ID',
  `modify_by` int(11) DEFAULT NULL COMMENT '修改人ID',
  `created` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `last_update_time` datetime DEFAULT NULL COMMENT '最后更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 角色与菜单的多对多的表
CREATE TABLE `sys_role_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_id` int(11) DEFAULT NULL COMMENT '角色ID',
  `menu_id` int(11) DEFAULT NULL COMMENT '菜单ID',
  `create_by` int(11) DEFAULT NULL COMMENT '创建人ID',
  `modify_by` int(11) DEFAULT NULL COMMENT '修改人ID',
  `created` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `last_update_time` datetime DEFAULT NULL COMMENT '最后一次更新时间',
  `is_deleted` tinyint(4) DEFAULT '0' COMMENT '逻辑删除 0未删除 1已删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 会员表
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) DEFAULT NULL COMMENT '用户名',
  `password` varchar(255) DEFAULT NULL COMMENT '密码',
  `status` tinyint(4) DEFAULT '1' COMMENT '状态',
  `create_by` int(11) DEFAULT NULL COMMENT '创建人ID',
  `mobile` varchar(20) DEFAULT NULL COMMENT '手机号码',
  `email` varchar(255) DEFAULT NULL COMMENT '邮箱',
  `created` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `last_update_time` datetime DEFAULT NULL COMMENT '最后更新时间',
  `is_deleted` tinyint(4) DEFAULT '0' COMMENT '逻辑删除 0未删除 1已删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

posted @   巫小诗  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示