微服务下的SpringSecurity认证端

从三板斧开始微服务下的SpringSecurity开始

一、引入组件包


    org.springframework.cloud
    spring-cloud-starter-oauth2

二、创建适配器

AuthorizationServerConfigurerAdapter是一个授权适配器,在这个适配器中有如下几个核心方法:

  • ClientDetailsServiceConfigurer

用来配置客户端详情服务(ClientDetailsService),这里的配置表明不是任何人都可以来授权中心进行授权的,客户端详情信息在这里进行初始化,可以在内存中也可以来自于数据库,根据你自己的需要进行配置;一个ClientDetails标识一个客户端的详情描述,ClientDetails具体的属性如下:

重要属性说明
属性名称 说明
clientId 是一个唯一标识,用于标识客户
secret 客户端的安全码;微信登录就有安全码
scope 用来限制客户端的访问范围,如果为空,标识所有范围都可以访问
authrizedGrantTypes 可以使用的授权类型(可选值范围:"authorization_code", "password", "client_credentials", "implicit", "refresh_token"),为空标识所有都支持;微信仅支持authorization_code;
authorities 客户端可以使用的权限(基于Spring Security Authorities)
redirectUris 回调地址,授权服务会往此地址回调推送客户端相关信息

ClientDetails客户端详情可以在运行时进行更新,可以通过访问底层存储介质(例如:JdbcClientDetailsService)或者自己实现ClientRegisterationService接口或者实现ClientDetailsService来进行定制;

  • AuthorizationServerSecurityConfigurer

配置令牌端点的安全约束;此接口包含了关于令牌管理的必要操作;

实现一个AuthorizationServerTokenServices接口需要继承DefaultTokenServices这个类;此类中包含了一些有用的实现,可以使用它来修改令牌的格式和令牌的存储模式;默认情况下,在创建一个令牌的时候,是使用随机值来进行填充的;这个类中完成了令牌管理的几乎所有事情,唯一需要依赖的是Spring容器中的一个TokenStore对象来定制令牌持久化;在Spring中有一个默认的TokenStore实现,即:ImMemoryTokenStore,这个类是将令牌保存到内存中,其他几个TokenStore实现类都可以根据自己的选择进行使用;

ImMemoryTokenStore:基于内存;

JdbcTokenStore:基于JDBC;

JwtTokenStore:基于JWT,这里啰嗦一下,此种类型时经常常用的类型,全称是“Json web Token”;是吧令牌信息全部编码到令牌本身,这样后端不需要维护令牌相关的信息,这是一大优势,当然缺点就是撤销令牌困难;传输占用空间比较大;

  • AuthorizationServerEndpointsConfigurer

用来配置令牌的访问端点和令牌服务;授权类型(Grant Types)有如下几点:

授权类型说明
授权类型 说明
authenticationManager 认证管理器,如果你选择的是password(资源所有者密码)这个授权类型时,需要指定authenticationManager对象来进行鉴权
userDetailsService 用户主体管理服务,在拥有自己UserDetailsService接口的实现,如果设置了这个属性,那么refresh_token刷新令牌的方式授权类型流程中会多一个检查的步骤,来确保是否依然有效
authorizationCodeServices 用于authorization_code授权码模式,用来设置授权服务;
implicitGrantService 用于设置隐式授权模式的状态;
tokenGranter 这个是深度拓展时使用的;一旦设置,授权将会全部交由自己来掌控,会忽略以上几个属性;


  1 @Configuration
  2 @EnableAuthorizationServer
  3 @EnableConfigurationProperties(value = JwtCaProperties.class)
  4 public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
  5 
  6     /**
  7      * 属性描述:注入数据源
  8      *
  9      * @date : 2020/4/12 0012 上午 11:54
 10      */
 11     private final DataSource dataSource;
 12 
 13     /**
 14      * 属性描述:JWT配置信息
 15      *
 16      * @date : 2020/4/12 0012 下午 10:49
 17      */
 18     private final JwtCaProperties jwtCaProperties;
 19 
 20     /**
 21      * 属性描述:用户信息处理
 22      *
 23      * @date : 2020/4/13 0013 上午 12:12
 24      */
 25     private final MyUserDetailServiceImpl myUserDetailServiceImpl;
 26 
 27     private final AuthenticationManager authenticationManager;
 28 
 29     /**
 30      * 功能描述:认证中心配置
 31      *
 32      * @author : XXSD
 33      * @date : 2020/4/12 0012 上午 11:54
 34      */
 35     public AuthServerConfig(DataSource dataSource, JwtCaProperties jwtCaProperties, MyUserDetailServiceImpl myUserDetailServiceImpl, AuthenticationManager authenticationManager) {
 36         this.dataSource = dataSource;
 37         this.jwtCaProperties = jwtCaProperties;
 38         this.myUserDetailServiceImpl = myUserDetailServiceImpl;
 39         this.authenticationManager = authenticationManager;
 40     }
 41 
 42     /**
 43      * 功能描述:配置第三方客户端的授权服务器安全配置
 44      * 
45 * 用来配置令牌的端点安全约束 46 * 47 * @author : XXSD 48 * @date : 2020/4/14 0014 下午 8:55 49 */ 50 @Override 51 public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { 52 //第三方客户端校验token需要带入 clientId 和clientSecret来校验 53 security.checkTokenAccess("isAuthenticated()") 54 //来获取我们的tokenKey需要带入clientId,clientSecret 55 .tokenKeyAccess("isAuthenticated()"); 56 //使用表单认证的方式进行申领令牌 57 security.allowFormAuthenticationForClients(); 58 } 59 60 /** 61 * 功能描述:配置接入的客户端 62 *
63 * 用来配置客户端详情(ClientDetailsService),客户端详情杂这里进行初始化,可以把客户端详情信息写死或这通过 64 * 数据库来存储详情信息 65 * 66 * @author : XXSD 67 * @date : 2020/4/12 0012 上午 11:49 68 */ 69 @Override 70 public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 71 /*//使用内存方式 72 clients.inMemory() 73 .withClient("这里写客户端的ID") 74 .secret(new BCryptPasswordEncoder().encode("这里放入密码")) 75 .resourceIds("这里是客户端的资源列表") 76 //此客户端可以使用的授权类型 77 .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token") 78 .scopes("允许的授权范围;没有什么特殊的意义,就是一个字符串,是自己根据我们后台的服务进行的一个划分而已") 79 //跳转到授权页面,是否显示授权页面;True标识默认用户同意,不显示授权页面 80 .autoApprove(false) 81 .redirectUris("这里写入你自己的回调地址"); 82 //如果有更多的,在这里添加and 83 .and() 84 */ 85 clients.withClientDetails(clientDetails()); 86 } 87 88 /** 89 * 功能描述:配置Token信息 90 *
91 * 用于配置令牌(Token)的访问端点和令牌服务(TokenService) 92 * 93 * @author : XXSD 94 * @date : 2020/4/12 0012 下午 11:23 95 */ 96 @Override 97 public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 98 99 /* 100 * AuthorizationServerEndpointsConfigurer对于不同类型的授权类别也需要配置不同的属性 101 * authenticationManager: 102 * 认证管理器,如果你选择的是password(资源所有者密码)这个授权类型时,需要指定authenticationManager对象来进行鉴权 103 * userDetailsService: 104 * 用户主体管理服务,在拥有自己UserDetailsService接口的实现,如果设置了这个属性,那么refresh_token刷新令牌的方式授权类型流程中会多一个检查的步骤,来确保是否依然有效 105 * authorizationCodeServices: 106 * 用于authorization_code授权码模式,用来设置授权服务; 107 * implicitGrantService: 108 * 用于设置隐式授权模式的状态; 109 * tokenGranter: 110 * 这个是深度拓展时使用的;一旦设置,授权将会全部交由自己来掌控,会忽略以上几个属性; 111 * */ 112 113 final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); 114 //加入了之后增强器才有效果 115 tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnchaner(), jwtAccessTokenConverter())); 116 /* 117 * OAuth2.0已经默认配置了几个端点: 118 * /oauth/authorize:授权端点; 119 * /oauth/token:令牌端点; 120 * /oauth/confirm_access:用户确认授权提交的端点; 121 * /oauth/error:授权服务错误信息端点; 122 * /oauth/check_token:用于资源服务访问的令牌进行解析的端点; 123 * /oauth/token_key:使用JWT令牌需要用到的提供共有密钥的端点; 124 * */ 125 endpoints 126 //自定义重新定义端点路径 127 // .pathMapping("") 128 .tokenStore(tokenStore()) 129 .tokenEnhancer(tokenEnhancerChain) 130 .userDetailsService(myUserDetailServiceImpl) 131 //指定令牌管理服务 132 // .tokenServices(tokenServices()) 133 //限制Token的信息允许提交的模式 134 // .allowedTokenEndpointRequestMethods(HttpMethod.POST); 135 .authenticationManager(authenticationManager); 136 } 137 138 /*private AuthorizationServerTokenServices tokenServices(){ 139 final DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); 140 //客户端详情服务 141 defaultTokenServices.setClientDetailsService(clientDetails()); 142 //令牌自动刷新 143 defaultTokenServices.setSupportRefreshToken(true); 144 //令牌存储策略 145 defaultTokenServices.setTokenStore(tokenStore()); 146 //令牌默认有效期,当前设置为2小时;单位秒 147 defaultTokenServices.setAccessTokenValiditySeconds(7200); 148 //刷新令牌默认有效期,当前设置为3天;单位秒 149 defaultTokenServices.setRefreshTokenValiditySeconds(259200); 150 151 return defaultTokenServices; 152 }*/ 153 154 /** 155 * 功能描述:注入基于JWT的自定义Token增强器 156 * 157 * @author : XXSD 158 * @date : 2020/4/12 0012 下午 11:21 159 */ 160 @Bean 161 public AuthTokenEnchaner tokenEnchaner() { 162 return new AuthTokenEnchaner(); 163 } 164 165 /** 166 * 功能描述:Token存储控制对象 167 * 168 * @author : XXSD 169 * @date : 2020/4/12 0012 下午 1:01 170 */ 171 @Bean 172 public TokenStore tokenStore() { 173 /* 174 * Token有如下几种方式存储: 175 * 1、基于内存:InMemoryTokenStore; 176 * 2、基于数据库:JdbcTokenStore; 177 * 3、基于Redis:RedisTokenStore; 178 * 4、基于JWT:JwtTokenStore 179 * 这里是基于JWT的 180 * */ 181 return new JwtTokenStore(jwtAccessTokenConverter()); 182 } 183 184 /** 185 * 功能描述:Token与用户信息之间的转换器 186 * 187 * @author : XXSD 188 * @date : 2020/4/12 0012 下午 1:01 189 */ 190 @Bean 191 public JwtAccessTokenConverter jwtAccessTokenConverter() { 192 final JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter(); 193 jwtAccessTokenConverter.setKeyPair(keyPair()); 194 return jwtAccessTokenConverter; 195 } 196 197 /** 198 * 功能描述:注入证书 199 * 200 * @author : XXSD 201 * @date : 2020/4/12 0012 下午 11:22 202 */ 203 @Bean 204 public KeyPair keyPair() { 205 KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource(jwtCaProperties.getKeyPairName()), jwtCaProperties.getKeyPairSecret().toCharArray()); 206 return keyStoreKeyFactory.getKeyPair(jwtCaProperties.getKeyPairAlias(), jwtCaProperties.getKeyPairStoreSecret().toCharArray()); 207 } 208 209 /** 210 * 功能描述:客户端处理服务 211 * 212 * @author : XXSD 213 * @date : 2020/4/12 0012 上午 11:58 214 */ 215 @Bean 216 public ClientDetailsService clientDetails() { 217 /* 218 * 如果是使用JDBC的方式进行,那么就需要在数据库中创建一个表 219 * 表的名称为:oauth_client_details 220 * CREATE TABLE `oauth_client_details` ( 221 -- 标识客户端的ID; 222 `client_id` varchar(256) CHARACTER SET utf8 NOT NULL, 223 `resource_ids` varchar(256) CHARACTER SET utf8 DEFAULT NULL, 224 -- 客户端安全码; 225 `client_secret` varchar(256) CHARACTER SET utf8 DEFAULT NULL, 226 -- 用来限制客户端的访问范围,如果为空标识客户端拥有全部的访问范围 227 `scope` varchar(256) CHARACTER SET utf8 DEFAULT NULL, 228 -- authrized;此客户端可以使用的授权类型,默认为空(全部可用:authorizaton_code、password、client_credentals、implicit、refresh_token);注:在微信中只支持authoriazton_code这一种 229 `authorized_grant_types` varchar(256) CHARACTER SET utf8 DEFAULT NULL, 230 -- 回调的地址;授权服务会往此地址推送客户端的相关信息 231 `web_server_redirect_uri` varchar(256) CHARACTER SET utf8 DEFAULT NULL, 232 -- 此客户端可以使用的权限(基于Spring Security authorities) 233 `authorities` varchar(256) CHARACTER SET utf8 DEFAULT NULL, 234 `access_token_validity` int(11) DEFAULT NULL, 235 `refresh_token_validity` int(11) DEFAULT NULL, 236 `additional_information` varchar(4096) CHARACTER SET utf8 DEFAULT NULL, 237 -- 跳转到授权页面,是否跳转到授权同意页面 238 `autoapprove` varchar(256) CHARACTER SET utf8 DEFAULT NULL, 239 PRIMARY KEY (`client_id`) 240 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 241 * */ 242 return new JdbcClientDetailsService(dataSource); 243 } 244 }

三、授权中心配置

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 属性描述:认证服务器加载用户信息对象
     *
     * @date : 2020/4/13 0013 上午 12:16
     */
    private final MyUserDetailServiceImpl myUserDetailServiceImpl;

    /**
     * 功能描述:授权中心安全配置
     *
     * @author : XXSD
     * @date : 2020/10/10 0010 下午 1:11
     */
    public WebSecurityConfig(MyUserDetailServiceImpl myUserDetailServiceImpl) {
        this.myUserDetailServiceImpl = myUserDetailServiceImpl;
    }

    /**
     * 功能描述:用于构建用户认证组件,需要传递userDetailsService和密码加密器
     *
     * @author : XXSD
     * @date : 2020/4/13 0013 上午 12:15
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailServiceImpl).passwordEncoder(passwordEncoder());
    }

    /**
     * 功能描述:配置安全拦截策略
     *
     * @author : XXSD
     * @date : 2021/1/9 0009 下午 6:34
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //关闭CSRF跨域检查
                .csrf().disable()
//                .authorizeRequests()
//                //配置其他请求要求登录
//                .anyRequest().authenticated()
//                //设置并行策略
//                .and()
//                // 可以从默认的login页面登录
//                .formLogin()
        ;
    }

    /**
     * 设置前台静态资源不拦截
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/assets/**", "/css/**", "/images/**");
    }

    /**
     * 功能描述:随机密码加密器
     *
     * @author : XXSD
     * @date : 2020/4/13 0013 上午 12:19
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}

你可能感兴趣的:(开发语言)