微服务下的SpringSecurity-认证端

665 阅读11分钟

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

一、引入组件包

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

二、创建适配器

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      * <br /> 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      * <br /> 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      * <br /> 91      * 用于配置令牌(Token)的访问端点和令牌服务(TokenService92      * 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         endpoints126                 //自定义重新定义端点路径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 : XXSD158      * @date : 2020/4/12 0012 下午 11:21159      */160     @Bean161     public AuthTokenEnchaner tokenEnchaner() {162         return new AuthTokenEnchaner();163     }164 165     /\*\*166      \* 功能描述:Token存储控制对象167      \*168      \* @author : XXSD169      * @date : 2020/4/12 0012 下午 1:01170      */171     @Bean172     public TokenStore tokenStore() {173         /\*174          \* Token有如下几种方式存储:175          * 1、基于内存:InMemoryTokenStore176          * 2、基于数据库:JdbcTokenStore177          * 3、基于RedisRedisTokenStore178          * 4、基于JWTJwtTokenStore179          * 这里是基于JWT180          * */181         return new JwtTokenStore(jwtAccessTokenConverter());182     }183 184     /\*\*185      \* 功能描述:Token与用户信息之间的转换器186      \*187      \* @author : XXSD188      * @date : 2020/4/12 0012 下午 1:01189      */190     @Bean191     public JwtAccessTokenConverter jwtAccessTokenConverter() {192         final JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();193         jwtAccessTokenConverter.setKeyPair(keyPair());194         return jwtAccessTokenConverter;195     }196 197     /\*\*198      \* 功能描述:注入证书199      \*200      \* @author : XXSD201      * @date : 2020/4/12 0012 下午 11:22202      */203     @Bean204     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 : XXSD213      * @date : 2020/4/12 0012 上午 11:58214      */215     @Bean216     public ClientDetailsService clientDetails() {217         /\*218         \* 如果是使用JDBC的方式进行,那么就需要在数据库中创建一个表219         * 表的名称为:oauth\_client\_details220         * 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
@EnableWebSecuritypublic 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();
    }
 
}