spring boot oauth2服务端搭建

spring boot oauth2服务端搭建

相关网站

官方文档首页:https://projects.spring.io/spring-security-oauth/docs/Home.html
源代码(oauth):https://github.com/spring-projects/spring-security-oauth
源代码(boot):https://github.com/spring-projects/spring-security-oauth2-boot
开发指南:https://projects.spring.io/spring-security-oauth/docs/oauth2.html
注:新版本的spring-security已对oauth2做了较大改变,整个集成过程已经完全不一样,学习过程中注意相关版本号

集成过程

  1. 搭建基本的spring-boot项目,这里不再重复,其中涉及的软件版本
<dependencyManagement>
    <dependencies>
        <dependency>
            <!-- Import dependency management from Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.2.4.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>
    <!--安全框架 start-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security.oauth.boot</groupId>
        <artifactId>spring-security-oauth2-autoconfigure</artifactId>
        <version>2.0.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security.oauth</groupId>
        <artifactId>spring-security-oauth2</artifactId>
        <version>2.2.1.RELEASE</version>
    </dependency>
    <!--安全框架 end-->
</dependencies>
  1. 授权服务器配置,最简化配置,先让程序能跑起来,后期再扩展
@Component
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient("ifusespasswordgranttype").authorizedGrantTypes("password")
                .secret("{noop}thenneedsauthenticationmanager").scopes("any");
    }


    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(this.authenticationManager)
                .userDetailsService(userDetailsService);
    }

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

    private static String CLIENT = "ifusespasswordgranttype:thenneedsauthenticationmanager";


    public static void main(String[] args) {
        byte[] encodedAuth = Base64.encodeBase64(CLIENT.getBytes());
        String authHeader = "Basic " + new String(encodedAuth);
        System.out.println(authHeader);
    }

}

说明:

  • 授权服务器client配置
  • 访问权限配置
  • 授权管理器配置,见SecurityConfig
  • main函数提供client加密算法,该解决需要请求时写到header中,如:Authorization=Basic xxxx
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    @Bean(BeanIds.AUTHENTICATION_MANAGER)
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        return new InMemoryUserDetailsManager(
                User.withUsername("admin1").password("{noop}111111").roles("USER").build()
        );
    }

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

    public static void main(String[] args) {
        PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        String encode1 = passwordEncoder.encode("{MD5}admin");
        String encode2 = passwordEncoder.encode("{MD5}111111");
        System.out.println(encode1 + "  " + encode2);
        boolean root = passwordEncoder.matches("{MD5}111111", encode1);
        boolean root1 = passwordEncoder.matches("{MD5}111111", encode2);
        System.out.println(root + "  " + root1);
    }
}

说明:

  • 覆盖授权管理器
  • 覆盖用户管理器
  • main函数提供密码加密及验证方法

个性定制

UserDetailsService动态取用户信息

@Component
public class UserDetailsServiceEnergy implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        List<GrantedAuthority> role_user = AuthorityUtils.createAuthorityList("ROLE_USER");
        SecurityUserDetails userDetails = new SecurityUserDetails(username, "{bcrypt}$2a$10$bzDNJk/XW67geVlpjQl3uuppRSsUqK7Gdw5NZbQQk/eTjHUV77n9S", role_user, 1L);
        return userDetails;
    }

    public static void main(String[] args) {
        PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        String encode1 = passwordEncoder.encode("{MD5}admin");
        String encode2 = passwordEncoder.encode("{MD5}111111");
        System.out.println(encode1 + "  " + encode2);
        boolean root = passwordEncoder.matches("{MD5}111111", encode1);
        boolean root1 = passwordEncoder.matches("{MD5}111111", encode2);
        System.out.println(root + "  " + root1);
    }
}

说明:

  • 这里将扩展单独抽离出来了,SecurityConfig 中UserDetailsService的配置需要取消
  • 命名不用参考,取名字太难了
  • SecurityUserDetails继承至org.springframework.security.core.userdetails.User可以附带一些业务信息,具体业务信息自行定制,该实例中附带了用户在数据库中的ID,为了简化代码,这里没有实际操作数据库
  • security约定在权限前面必须加“ROLE_”与授权服务器中的roles匹配
  • 密码从之前的明文改为了MD5加密,登录时密码格式为:{MD5}111111

ClientDetailsService动态取client信息

@Component
public class ClientDetailsServiceEnergy implements ClientDetailsService {

    @Override
    public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
        List<String> scope = new ArrayList<>();
        scope.add("any");
        List<String> grantTypes = new ArrayList<>();
        grantTypes.add("password");//支持password授权模式
        grantTypes.add("client_credentials");//支持client_credentials授权模式
        BaseClientDetails clientDetails = new BaseClientDetails();
        clientDetails.setClientId("ifusespasswordgranttype");
        clientDetails.setClientSecret("{noop}thenneedsauthenticationmanager");
        clientDetails.setScope(scope);
        clientDetails.setAccessTokenValiditySeconds(36000);
        clientDetails.setAuthorizedGrantTypes(grantTypes);
        clientDetails.setAutoApproveScopes(scope);
        return clientDetails;
    }

    private static String CLIENT = "ifusespasswordgranttype:thenneedsauthenticationmanager";


    public static void main(String[] args) {
        byte[] encodedAuth = Base64.encodeBase64(CLIENT.getBytes());
        String authHeader = "Basic " + new String(encodedAuth);
        System.out.println(authHeader);
    }

}

说明:

  • 该扩展可用于API授权登录方式,与用户登录区别,提高账号安全
  • 该扩展需要AuthorizationServerConfig中相关配置
  • ClientDetailsService 注入时需要指定名称,不指定会报错

扩展完成后的完整代码1.0

注:其他未贴出的代码与上面保持一致

@Component
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Qualifier("clientDetailsServiceEnergy")
    @Autowired
    private ClientDetailsService clientDetailsService;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService);
//        clients.inMemory().withClient("ifusespasswordgranttype").authorizedGrantTypes("password")
//                .secret("{noop}thenneedsauthenticationmanager").scopes("any");
    }


    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(this.authenticationManager)
                .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()")
                //允许client_credentials方式授权
                .allowFormAuthenticationForClients();
    }

}
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Override
    @Bean(BeanIds.AUTHENTICATION_MANAGER)
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

//    @Override
//    @Bean
//    public UserDetailsService userDetailsService() {
//        return new InMemoryUserDetailsManager(
//                User.withUsername("admin1").password("{noop}111111").roles("USER").build()
//        );
//    }

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


}

登录方式

密码登录

header:

  • Authorization=Basic aWZ1c2VzcGFzc3dvcmRncmFudHR5cGU6dGhlbm5lZWRzYXV0aGVudGljYXRpb25tYW5hZ2Vy

form:

  • grant_type=password
  • username=admin2
  • password={MD5}111111

client登录方式

form:

  • grant_type=client_credentials
  • client_id=ifusespasswordgranttype
  • client_secret=thenneedsauthenticationmanager

注:到这里整个集成基本完成,剩下的工作是完善各种错误处理及异常提示
注:前端多系统采用事件通知的方式可实现token共享,达到单点登录目的

异常处理扩展

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值