SpringCloud Oauth2(1)授权中心搭建

SpringCloud Oauth2(1)授权中心搭建

这里分为两部分,第一部分是授权中心的搭建,第二部分是其他微服务搭配资源中心的鉴权,好了,话不多说,开撸。

项目结构

项目结构如图:
SpringCloud Oauth2(1)授权中心搭建_第1张图片
MyPrincipalMyUserDetails是为了自定义用户的信息,可以方便http://localhost:1006/auth/user?access_token=41eb0a06-0145-4bec-97f7-b80719b616d2这一类获取用户信息的接口扩展一些有用的信息。

OAuth2ServerConfig是SpringCloud Oauth2的配置.
SecurityConfiguration是Spring security的配置

maven引用

pom.xml

 <dependencies>
        <dependency>
            <groupId>com.td</groupId>
            <artifactId>common-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 注意是starter,自动配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- 不是starter,手动配置 -->
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>td</groupId>
            <artifactId>myjdbc</artifactId>
            <version>4.7</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
    </dependencies>

common-server是我实体类和工具类的公共包
myjdbc是我自己写的jdbc的轮子。

oauth2配置

OAuth2ServerConfig

@Configuration
public class OAuth2ServerConfig {

    private static final String DEMO_RESOURCE_ID = "order";

    @Configuration
    @EnableResourceServer
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.resourceId(DEMO_RESOURCE_ID).stateless(true);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests()
                    .antMatchers("/v2/api-docs/**","/role/**","/rule/**","/users/**","/testFeign/**").permitAll()//配置不需要权限校验
                    .anyRequest().authenticated()
                .and()
                    .httpBasic()
            ;
        }
    }


    @Configuration
    @EnableAuthorizationServer
    protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

        @Autowired
        AuthenticationManager authenticationManager;
        @Autowired
        RedisConnectionFactory redisConnectionFactory;
        @Autowired
        UserDetailServiceImpl userDetailsService;

        @Autowired
        private DataSource dataSource;

        @Bean // 声明 ClientDetails实现
        public ClientDetailsService clientDetails() {
            return new JdbcClientDetailsService(dataSource);
        }


        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

//        password 方案一:明文存储,用于测试,不能用于生产
//        String finalSecret = "123456";
//        password 方案二:用 BCrypt 对密码编码
//        String finalSecret = new BCryptPasswordEncoder().encode("123456");
            // password 方案三:支持多种编码,通过密码的前缀区分编码方式
//            String finalSecret = "{bcrypt}"+new BCryptPasswordEncoder().encode("123456");
//            //配置两个客户端,一个用于password认证一个用于client认证
//            clients.inMemory().withClient("client_1")
//                    .resourceIds(DEMO_RESOURCE_ID)
//                    .authorizedGrantTypes("client_credentials", "refresh_token")
//                    .scopes("select")
//                    .authorities("oauth2")
//                    .secret(finalSecret)
//                    .and().withClient("client_2")
//                    .resourceIds(new String[]{"order","eurekaDemo1"})
//                    .authorizedGrantTypes("password", "refresh_token")
//                    .scopes("select")
//                    .authorities("oauth2")
//                    .secret(finalSecret);
            clients.withClientDetails(clientDetails());
        }

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
            endpoints
                    .tokenStore(new RedisTokenStore(redisConnectionFactory))
                    .authenticationManager(authenticationManager)
                    .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
                    .userDetailsService(userDetailsService);//若无,refresh_token会有UserDetailsService is required错误
        }

        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
            //允许表单认证
            oauthServer.allowFormAuthenticationForClients();
            oauthServer
                    .tokenKeyAccess("permitAll()")
                    .checkTokenAccess("isAuthenticated()");
        }

    }

}

接着是数据库jdbc的实现
UserDetailServiceImpl

@Service
public class UserDetailServiceImpl implements UserDetailsService {

    @Autowired
    SysUserDao sysUserDao;
    @Autowired
    SysUserAuthDao authDao;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        //spring加密
//        PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();

        String lowercaseLogin = s.toLowerCase();
        //获取用户
        SysUser sysUser = sysUserDao.getByUserName(lowercaseLogin);

        if (sysUser == null) {
           throw new UsernameNotFoundException("用户:" + lowercaseLogin + "不存在!");
        }

        List<SysUserAuth> userAuthList = authDao.findAll(sysUser.getId()+"");
        Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        userAuthList.forEach((sysUserAuth) -> {
            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(sysUserAuth.getSysRoleCode());
            grantedAuthorities.add(grantedAuthority);
        });

        //返回一个SpringSecurity需要的用户对象
        UserDetails userDetails = new org.springframework.security.core.userdetails.User(
                sysUser.getUsername(),
//                passwordEncoder.encode(user.getPassword()),
                sysUser.getPassword(),
                grantedAuthorities);

        UserDetails myuser = new MyPrincipal( sysUser.getUsername(),sysUser.getPassword(),grantedAuthorities);
        return myuser;
//        return userDetails;
    }
}

注意grantedAuthorities这个权限的集合和最终的返回值UserDetails。最后的MyPrincipal这个对象就是我自定义的那个user对象。

security配置

SecurityConfiguration

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


//    @Override
//    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder());
//    }

//这部分是基于内存的
//    @Bean
//    @Override
//    protected UserDetailsService userDetailsService(){
//        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        password 方案一:明文存储,用于测试,不能用于生产
        String finalPassword = "123456";
        password 方案二:用 BCrypt 对密码编码
        String finalPassword = bCryptPasswordEncoder.encode("123456");
//        // password 方案三:支持多种编码,通过密码的前缀区分编码方式
//        String finalPassword = "{bcrypt}"+bCryptPasswordEncoder.encode("123456");
//        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//        manager.createUser(User.withUsername("admin").password(finalPassword).authorities("ADMIN").roles("ADMIN","USER").build());
//        manager.createUser(User.withUsername("user_1").password(finalPassword).authorities("USER").roles("USER").build());
//        manager.createUser(User.withUsername("user_2").password(finalPassword).authorities("USER").roles("USER").build());
//        return manager;
//    }
    @Autowired
    private UserDetailServiceImpl userDetailService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailService);
    }

    /**
     * springboot2.0 删除了原来的 plainTextPasswordEncoder
     * https://docs.spring.io/spring-security/site/docs/5.0.4.RELEASE/reference/htmlsingle/#10.3.2 DelegatingPasswordEncoder
     *
     */


    // password 方案一:明文存储,用于测试,不能用于生产
//    @Bean
//    PasswordEncoder passwordEncoder(){
//        return NoOpPasswordEncoder.getInstance();
//    }

    // password 方案二:用 BCrypt 对密码编码
//    @Bean
//    PasswordEncoder passwordEncoder(){
//        return new BCryptPasswordEncoder();
//    }

    // password 方案三:支持多种编码,通过密码的前缀区分编码方式,推荐
    @Bean
    PasswordEncoder passwordEncoder(){
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

//
//    /**
//     * 这一步的配置是必不可少的,否则SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户
//     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        AuthenticationManager manager = super.authenticationManagerBean();
        return manager;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
            .requestMatchers().anyRequest()
            .and()
                .authorizeRequests()
                .antMatchers("/oauth/**").permitAll();
        // @formatter:on
    }
}

Oauth2的基础我就不说了,测试上面的代码:
SpringCloud Oauth2(1)授权中心搭建_第2张图片
然后是刷新token
SpringCloud Oauth2(1)授权中心搭建_第3张图片
谢谢大家阅读。

你可能感兴趣的:(springcloud)