spring boot oauth2单点登录(二)客户端信息存储

相关文章

1、spring boot oauth2单点登录(一)-前后端分离例子
2、spring boot oauth2单点登录(二)-客户端信息存储
3、spring boot oauth2单点登录(三)-token存储方式
4、spring boot oauth2单点登录(四)-code存储方式

源码地址

后端:https://gitee.com/fengchangxin/sso
前端:https://gitee.com/fengchangxin/sso-page

准备

后端:三个spring boot应用,auth(授权管理),client1(客户端应用1),client2(客户端应用2)。
前端:三个Vue项目,auth,client1,client2。分别对应三个后端应用。
工具:nginx
域名:oauth.com,client1.com,client2.com,分别对应三个系统。
开发环境:先在host文件添加上面三个域名。
端口:
后端服务auth(8080),client1(8081),client2(8082)。
前端auth(8083),client1(8084),client2(8085)。
依赖:


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

测试地址:
client1:http://client1.com/client1Page/#/home
client2:http://client2.com/client2Page/#/home
登录用户:admin/123456

备注:此篇文章对应的授权中心服务是:auth-db。
spring boot oauth2单点登录(一)-实现例子通过这篇文章搭建了开发环境单点登录,不过用户信息和客户端信息都是存储在内存的,现在把这些信息存储在数据库。

1、用户信息

实现UserDetailsService接口的loadUserByUsername方法,用户登录时通过此接口查询数据库返回账户密码等信息。并在WebSecurityConfiguration添加配置。

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //TODO 在这里进行数据库查询获取用户信息
        UserDetails user = User.withUsername("admin").password(passwordEncoder.encode("123456")).authorities("ADMIN", "USER").build();
        return user;
    }
}
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }

2、客户端信息

2.1默认实现

框架有默认的实现,我们只要在AuthorizationServerConfiguration配置好使用即可,数据库表结构在resources下oauth.sql。需要注意的是在oauth_client_details表存储的client_secret是经过PasswordEncoder加密处理的数据,不然无法登录。
使用默认实现需要引入jdbc依赖:

        
            org.springframework.boot
            spring-boot-starter-jdbc
        
@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    @Autowired
    private PasswordEncoder passwordEncoder;

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

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(new JdbcClientDetailsServiceBuilder().dataSource(dataSource)
                .passwordEncoder(passwordEncoder).build());
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);
    }

}

2.2自定义实现

若表结构不符合要求,想要改变表结构,不想用默认的实现,那可以根据自己的需求来实现,主要是实现ClientDetailsService接口,并把结果组装成BaseClientDetails返回。
举个例子,自定义一张表client_details_test,结构和oauth_client_details一样,多加了一个Id字段,然后实现ClientDetailsService接口,需要注意的是这个实现类的bean名称不能使用默认,需要重新命名。

@Service("clientDetailsManager")
public class ClientDetailsServiceImpl implements ClientDetailsService {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
        //TODO 自定义实现数据库查询
        String sql = "select client_id,client_secret,resource_ids, scope, authorized_grant_types, web_server_redirect_uri, " +
                "authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove from client_details_test where client_id=?";
        return jdbcTemplate.queryForObject(sql,new ClientDetailsTestRowMapper(),clientId);
    }

    private static class ClientDetailsTestRowMapper implements RowMapper {
        @Override
        public ClientDetails mapRow(ResultSet rs, int i) throws SQLException {
            BaseClientDetails details = new BaseClientDetails(rs.getString(1), rs.getString(3), rs.getString(4),
                    rs.getString(5), rs.getString(7), rs.getString(6));
            details.setClientSecret(rs.getString(2));
            if (rs.getObject(8) != null) {
                details.setAccessTokenValiditySeconds(rs.getInt(8));
            }
            if (rs.getObject(9) != null) {
                details.setRefreshTokenValiditySeconds(rs.getInt(9));
            }
            String scopes = rs.getString(11);
            if (scopes != null) {
                details.setAutoApproveScopes(StringUtils.commaDelimitedListToSet(scopes));
            }
            return details;
        }
    }
}

然后在AuthorizationServerConfiguration中配置。

    @Autowired
    private ClientDetailsService clientDetailsManager;
    //自定义实现
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsManager);
    }

总结:上面客户端信息的两种方式而选一,根据实际情况选择,默认实现表结构、表名是不可以变化的,自定义实现可以结合mybatis等其他数据库框架来灵活使用,表结构名称等都可以自己设计,比较灵活。而用户登录信息接口UserDetailsService也有默认实现,比如数据库实现JdbcDaoImpl。
结合文章开头列出的第一篇文章和此篇文章,就满足了一般的单机应用,但对于集群部署的应用还是不够,token和授权码code都是存在内存中,集群部署的会出问题,下列文章解决此问题。

你可能感兴趣的:(spring boot oauth2单点登录(二)客户端信息存储)