OAuth + Security -1 – 认证服务器配置

设置

基础包依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>
================================== 在spring-boot中 ==================================
<dependency>
   <groupId>org.springframework.security.oauth</groupId>
   <artifactId>spring-security-oauth2</artifactId>
   <version>2.3.5.RELEASE</version>
</dependency>
================================== 或者在spring-cloud中 ==================================
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

设置三大焦点

认证服务器的设置需要继续 AuthorizationServerConfigurerAdapter 类,然后重写内部的方式来获得自己逻辑的token,其源码如下:

public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {

    // 设置平安约束,主要是对默认的6个端点的开启与关闭设置
	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
	}

    // 客户端信息设置
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
	}

    // 认证服务器令牌接见端点设置和令牌服务设置,可以替换默认的端点url,设置支持的授权模式
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
	}

}
  1. 设置客户端详细信息

ClientDetailsServiceConfigurer能够使用内存或者JDBC来实现客户端详情服务
(ClientDetailsService ) , ClientDetailsService卖力查找ClientDetails ,而ClientDetails有几个主要的属性如下列表:

  • clientld: (必须的)用来标识客户的Id。
  • secret : ( 需要值得信托的客户端)客户端平安码,若是有的话。
  • scope :用来限制客户端的接见局限,若是为空(默认)的话,那么客户端拥有所有的接见局限。
  • authorizedGrantTypes :此客户端可以使用的授权类型,默以为空。
  • authorities :此客户端可以使用的权限(基于Spring Security authorities )。

客户端详情( ClientDetails )能够在应用程序运行的时刻举行更新,可以通过接见底层的存储服务(例如
将客户端详情存储在一个关系数据库的表中,就可以使用JdbcClientDetailsService或者通过自己实现
ClientRegistrationService接口(同时你也可以实现ClientDetailsService接口)来举行治理。

  1. 令牌接见端点和令牌服务设置

AuthorizationServerEndpointsConfigurer这个工具的实例可以完成令牌服务以及令牌endpoint设置。

设置授权类型( Grant Types )

AuthorizationServerEndpointsConfigurer通过设定以下属性决议支持的授权类型( Grant Types ) :

  • authenticationManager:认证治理器,当你选择了资源所有者密码(password)授权类型的时刻,请设置这个属性注入一个AuthenticationManager工具。
  • userDetailsService :若是你设置了这个属性的话,那说明你有一个自己的UserDetailsService接口的实现,或者你可以把这个器械设置到全局域上面去(例如GlobalAuthenticationManagerConfigurer这个设置工具),当你设置了这个之后,那么”refresh_token”即刷新令牌授权类型模式的流程中就会包罗一个检查,用来确保这个账号是否仍然有用,假如说你禁用了这个账户的话。
  • authorizationCodeServices:这个属性是用来设置授权码服务的(即AuthorizationCodeServices的实例工具) ,主要用于”authorization_ code” 授权码类型模式。
  • implicitGrantService :这个属性用于设置隐式授权模式,用来治理隐式授权模式的状态。
  • tokenGranter :当你设置了这个器械(即TokenGranter接口实现),那么授权将会交由你来完全掌控,并且会忽略掉上面的这几个属性,这个属性一样平常是用作拓展用途的,即尺度的四种授权模式已经知足不了你的需求的时刻,才会思量使用这个。
设置授权端点的URL

默认的端点URL有以下6个:

  • /oauth/authorize : 授权端点。
  • /oauth/token : 令牌端点。
  • /oauth/confirm _access : 用户确认授权提交端点。
  • /oauth/error : 授权服务错误信息端点。
  • /oauth/check_token : 用于资源服务接见的令牌剖析端点。
  • /oauth/token_key : 提供公有密匙的端点,若是你使用JWT令牌的话。

这些端点都可以通过设置的方式更改其路径,在AuthorizationServerEndpointsConfigurer中有一个叫pathMapping()的方式用来设置端点URL链接,他有两个参数:

  • 第一个参数:string类型,默认的URL
  • 第二个参数:string类型,举行替换的URL

以上的参数都是以”/”最先的字符串

  1. 设置令牌端点的平安约束

AuthorizationServerSecurityConfigurer : 用来设置令牌端点(Token Endpoint)的平安约束,如设置资源服务器需要验证token的/oauth/check_token

总结

授权服务设置分成三大块,可以关联影象。

既然要完成认证,它首先得知道客户端信息从哪儿读取,因此要举行客户端详情设置。

既然要发表token,那必须得界说token的相关endpoint,以及token若何存取,以及客户端支持哪些类型的token。

既然露出除了一些endpoint,那对这些endpoint可以界说一些平安上的约束等。

详细代码如下:

Flutter,webview里面实现上传和下载的功能

  • 认证服务器设置类
/**
 * @author zhongyj <1126834403@qq.com><br/>
 * @date 2020/6/1
 */
@Configuration
@EnableAuthorizationServer
public class DimplesAuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    private PasswordEncoder passwordEncoder;

    private TokenStore tokenStore;

    private ClientDetailsService clientDetailsService;

    private AuthenticationManager authenticationManager;

    @Autowired
    public DimplesAuthorizationServerConfiguration(PasswordEncoder passwordEncoder, TokenStore tokenStore, ClientDetailsService clientDetailsService, AuthenticationManager authenticationManager) {
        this.passwordEncoder = passwordEncoder;
        this.tokenStore = tokenStore;
        this.clientDetailsService = clientDetailsService;
        this.authenticationManager = authenticationManager;
    }

    /**
     * 令牌端点的平安约束
     *
     * @param security AuthorizationServerSecurityConfigurer
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }

    /**
     * 客户端详情服务,暂时设置在内存中,后期改为存在数据库
     *
     * @param clients ClientDetailsServiceConfigurer
     * @throws Exception Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("dimples")
                .secret(this.passwordEncoder.encode("123456"))
                // 为了测试,以是开启所有的方式,现实营业根据需要选择
                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
                .accessTokenValiditySeconds(3600)
                .refreshTokenValiditySeconds(864000)
                .scopes("select")
                // false跳转到授权页面,在授权码模式中会使用到
                .autoApprove(false)
                // 验证回调地址
                .redirectUris("http://www.baidu.com");
    }

    /**
     * 令牌接见端点和令牌接见服务
     *
     * @param endpoints AuthorizationServerEndpointsConfigurer
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                // /oauth/token_key公然
                .authenticationManager(authenticationManager)
                // /oauth/check_token公然
                .authorizationCodeServices(authorizationCodeServices())
                // 允许表单获取token
                .tokenServices(tokenServices());

    }

    /**
     * 令牌治理服务
     *
     * @return TokenServices
     */
    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices services = new DefaultTokenServices();
        // 客户端详情服务
        services.setClientDetailsService(clientDetailsService);
        // 支持令牌刷新
        services.setSupportRefreshToken(true);
        // 令牌存储计谋
        services.setTokenStore(tokenStore);
        // 令牌默认有用期2小时
        services.setAccessTokenValiditySeconds(7200);
        // 刷新令牌默认有用期2天
        services.setRefreshTokenValiditySeconds(259200);
        return services;
    }

    /**
     * 设置授权码模式的授权码若何存储,暂时接纳内存
     *
     * @return AuthorizationCodeServices
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new InMemoryAuthorizationCodeServices();
    }

}

  • Token存储设置类
/**
 * @author zhongyj <1126834403@qq.com><br/>
 * @date 2020/6/1
 */
@Configuration
public class TokenConfigure {

    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }

}
  • Security 平安设置
/**
 * @author zhongyj <1126834403@qq.com><br/>
 * @date 2020/6/1
 */
@Configuration
public class DimplesWebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    private DimplesUserDetailServiceImpl userDetailsService;

    private PasswordEncoder passwordEncoder;

    @Autowired
    public DimplesWebSecurityConfiguration(DimplesUserDetailServiceImpl userDetailsService, PasswordEncoder passwordEncoder) {
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    /**
     * 平安阻挡机制(最主要)
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/oauth/**").permitAll()
                .anyRequest().authenticated()
                .and().formLogin();
    }

    /**
     * 非必须设置,可以不配
     * 认证治理设置
     * 毗邻数据查询用户信息,与数据库中密码比对
     *
     * @param auth AuthenticationManagerBuilder
     * @throws Exception Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }


    /**
     * 认证治理区设置,密码模式需要用到
     *
     * @return AuthenticationManager
     * @throws Exception Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}
  • 设置密码
@Configuration
public class DimplesConfigure {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}
  • 用户信息获取类
@Configuration
public class DimplesUserDetailServiceImpl implements UserDetailsService {

    private PasswordEncoder passwordEncoder;

    @Autowired
    public DimplesUserDetailServiceImpl(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 模拟一个用户,替换数据库获取逻辑
        DimplesUser user = new DimplesUser();
        user.setUserName(username);
        user.setPassword(this.passwordEncoder.encode("123456"));
        // 输出加密后的密码
        System.out.println(user.getPassword());

        return new User(username, user.getPassword(), user.isEnabled(),
                user.isAccountNonExpired(), user.isCredentialsNonExpired(),
                user.isAccountNonLocked(), AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}

@Data
public class DimplesUser implements Serializable {
    private static final long serialVersionUID = 3497935890426858541L;

    private String userName;

    private String password;

    private boolean accountNonExpired = true;

    private boolean accountNonLocked= true;

    private boolean credentialsNonExpired= true;

    private boolean enabled= true;
}

可能泛起的问题

  • 最可能泛起的问题,项目启动时报循环依赖的错误,要注意代码中在当前设置的类中写的Bean不能再当前类中去使用@Autowired注入
  • 在登录时控制台报错客栈溢出

错误追踪:

是由于设置密码模式下的AuthenticationManager时,方式名称为authenticationManager,更改为authenticationManagerBean即可

测试

授权码模式下获取token

  1. 首先获取授权码
    在浏览器输入 http://127.0.0.1:8080/oauth/authorize?client_id=dimples&response_type=code&redirect_uri=http://www.baidu.com

跳转到登录界面,然后举行用户登录,登录乐成以后选择用户授权,获取响应的授权码,将会显示在重定向URL的链接后面。如下图:

  1. 获取code

OAuth + Security -1 - 认证服务器配置

  1. 验证用户

OAuth + Security -1 - 认证服务器配置

  1. 授权

OAuth + Security -1 - 认证服务器配置

  1. 获得code

OAuth + Security -1 - 认证服务器配置

  1. 拿着这个获取的code的值去/oauth/token端点获取token,如下图:

OAuth + Security -1 - 认证服务器配置

这个code有错将不能获取到token,由于这个将会保存在程序内存中,同时这个code只能获取一次token

密码模式

/oauth/token?client_id=dimples&client_secret=123456&grant_type=password&username=dimples&password=123456

这种模式十分简朴,然则也意味着直接将用户的敏感信息泄露给了client端,因此这种模式一样平常只用于client是我们自己开发的情况下。

OAuth + Security -1 - 认证服务器配置

客户端模式

/oauth/token?client_id=dimples&client_secret=123456&grant_type=client_credentials

OAuth + Security -1 - 认证服务器配置

简化模式使用较少,也比较简朴,此处不做测试。

原创文章,作者:admin,如若转载,请注明出处:https://www.2lxm.com/archives/13408.html