如何使用Spring boot auth2?

本文介绍如何使用Spring Security OAuth2实现资源服务器和授权服务器的配置,并通过数据库持久化token信息,解决ClientAlreadyExistsException异常。

1、依赖,使用Maven

<dependency>

    <groupId>org.springframework.security.oauth</groupId>

    <artifactId>spring-security-oauth2</artifactId>

</dependency>

2、Resource Server 配置

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/api/menu_parent/**").authenticated();
    }
}

3、Authorization Server 配置,这里使用数据库持久化token信息,建表sql在后面,为了解决获取token时的ClientAlreadyExistsException异常,重写了JdbcClientDetailsServiceBuilder类,主要区别是

  

 @Override
    protected ClientDetailsService performBuild() {
        Assert.state(dataSource != null, "You need to provide a DataSource");
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        if (passwordEncoder != null) {
            // This is used to encode secrets as they are added to the database (if it isn't set then the user has top
            // pass in pre-encoded secrets)
            clientDetailsService.setPasswordEncoder(passwordEncoder);
        }
        for (ClientDetails client : clientDetails) {
            try {
                clientDetailsService.updateClientDetails(client);
            } catch (NoSuchClientException e) {
                clientDetailsService.addClientDetails(client);
            }
        }
        return clientDetailsService;
    }

这个方法,存在则更新client信息。

@Configuration
@EnableAuthorizationServer
@EnableConfigurationProperties
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;


    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // clients.jdbc(dataSource);
        MyCustomJdbcClientDetailsServiceBuilder jdbc=new MyCustomJdbcClientDetailsServiceBuilder();
        jdbc.dataSource(dataSource()).withClient("client")
                .authorizedGrantTypes("client-credentials", "password", "refresh_token")
                .authorities("ROLE_CLIENT", "ROLE_ANDROID_CLIENT").scopes("read", "write", "trust")
                .resourceIds("oauth2-resource").accessTokenValiditySeconds(5000).secret("secret")
                .refreshTokenValiditySeconds(50000);
        clients.setBuilder(jdbc);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
                .tokenEnhancer(new CustomTokenEnhancer()).userDetailsService(userDetailsService);

    }

    @Bean
    @ConfigurationProperties("spring.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public PasswordEncoder encoder() {
        return new PasswordEncoder() {

            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                return StringUtils.hasText(encodedPassword)
                        ? MD5Utils.encoder(rawPassword.toString()).equals(encodedPassword)
                        : false;
            }

            @Override
            public String encode(CharSequence rawPassword) {
                return MD5Utils.encoder(rawPassword.toString());
            }
        };
    }
}

4、UserDetailsService,实现loadUserByUsername 方法

    

@Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRep.loadUserByUsername(username);
        return user;
    }

5、向access_token添加额外信息 (非必要)

public class CustomTokenEnhancer implements TokenEnhancer {

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        User user = (User) authentication.getPrincipal();
        final Map<String, Object> additionalInfo = new HashMap<>();

        additionalInfo.put("id", user.getId());
        additionalInfo.put("name", user.getName());
        additionalInfo.put("authorities", user.getAuthorities());

        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);

        return accessToken;
    }

}

Token持久化建表语句:

create table if not exists oauth_client_details (
  client_id VARCHAR(255) PRIMARY KEY,
  resource_ids VARCHAR(255),
  client_secret VARCHAR(255),
  scope VARCHAR(255),
  authorized_grant_types VARCHAR(255),
  web_server_redirect_uri VARCHAR(255),
  authorities VARCHAR(255),
  access_token_validity INTEGER,
  refresh_token_validity INTEGER,
  additional_information VARCHAR(4096),
  autoapprove VARCHAR(255)
);

create table if not exists oauth_client_token (
  token_id VARCHAR(255),
  token LONG VARBINARY,
  authentication_id VARCHAR(255) PRIMARY KEY,
  user_name VARCHAR(255),
  client_id VARCHAR(255)
);

create table if not exists oauth_access_token (
  token_id VARCHAR(255),
  token LONG VARBINARY,
  authentication_id VARCHAR(255) PRIMARY KEY,
  user_name VARCHAR(255),
  client_id VARCHAR(255),
  authentication LONG VARBINARY,
  refresh_token VARCHAR(255)
);

create table if not exists oauth_refresh_token (
  token_id VARCHAR(255),
  token LONG VARBINARY,
  authentication LONG VARBINARY
);

create table if not exists oauth_code (
  code VARCHAR(255), authentication LONG VARBINARY
);

create table if not exists oauth_approvals (
    userId VARCHAR(255),
    clientId VARCHAR(255),
    scope VARCHAR(255),
    status VARCHAR(10),
    expiresAt TIMESTAMP,
    lastModifiedAt TIMESTAMP
);

create table if not exists ClientDetails (
  appId VARCHAR(255) PRIMARY KEY,
  resourceIds VARCHAR(255),
  appSecret VARCHAR(255),
  scope VARCHAR(255),
  grantTypes VARCHAR(255),
  redirectUrl VARCHAR(255),
  authorities VARCHAR(255),
  access_token_validity INTEGER,
  refresh_token_validity INTEGER,
  additionalInformation VARCHAR(4096),
  autoApproveScopes VARCHAR(255)
);

MyCustomJdbcClientDetailsServiceBuilder :

public class MyCustomJdbcClientDetailsServiceBuilder extends JdbcClientDetailsServiceBuilder {
    
    private Set<ClientDetails> clientDetails = new HashSet<ClientDetails>();

    private DataSource dataSource;

    private PasswordEncoder passwordEncoder; // for writing client secrets

    public JdbcClientDetailsServiceBuilder dataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        return this;
    }

    public JdbcClientDetailsServiceBuilder passwordEncoder(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
        return this;
    }

    @Override
    protected void addClient(String clientId, ClientDetails value) {
        clientDetails.add(value);
    }

    @Override
    protected ClientDetailsService performBuild() {
        Assert.state(dataSource != null, "You need to provide a DataSource");
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        if (passwordEncoder != null) {
            // This is used to encode secrets as they are added to the database (if it isn't set then the user has top
            // pass in pre-encoded secrets)
            clientDetailsService.setPasswordEncoder(passwordEncoder);
        }
        for (ClientDetails client : clientDetails) {
            try {
                clientDetailsService.updateClientDetails(client);
            } catch (NoSuchClientException e) {
                clientDetailsService.addClientDetails(client);
            }
        }
        return clientDetailsService;
    }

获取token:

224232_xgMx_3771998.png

224430_Rrpl_3771998.png

224502_qttx_3771998.png

224322_E0IM_3771998.png

访问未授权资源:

224704_mDk9_3771998.png

带上授权token,再次访问:

224848_k8UV_3771998.png

转载于:https://my.oschina.net/benz001/blog/1614220

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值