Springboot Security +Oauth2 微服务单点登录授权认证

1.介绍

 客户端向资源服务器发起请求后,资源服务器会首先拿接受的token去权限服务器验证,如果验证通过,才继续执行请求。

1.1 提供的四个授权模式:

1.授权码模式(authorization_code):
2.客户端认证
3.密码认证
4.隐式授权认证
5.刷新密钥

我们会在认证请求的grant_type参数中指定采用何种方式去进行认证。比如下面的一个链接,通过grant_type=authorization_code指定采用授权码认证。

http://localhost:8080/oauth/token?grant_type=authorization_code&client_id=client&client_secret=123456&code=jRvetc&redirect_uri=http://www.baidu.com

下面是一些默认的端点 URL:
/oauth/authorize:授权端点
/oauth/token:令牌端点
/oauth/confirm_access:用户确认授权提交端点
/oauth/error:授权服务错误信息端点
/oauth/check_token:用于资源服务访问的令牌解析端点
/oauth/token_key:提供公有密匙的端点,如果你使用JWT令牌的话
授权端点的 URL 应该被 Spring Security 保护起来只供授权用户访问

2.代码配置

2.1配置AuthorizationServerConfigurerAdapter.java
@Configuration
@EnableAuthorizationServer
public class Oauth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {


    @Autowired
    private OauthClientDetailServiceImpl oauthClientDetailService;

    @Autowired
    private AuthenticationManager authenticationManager;


    @Autowired
    private SecurityConfig securityConfig;


    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    RedisConnectionFactory redisConnectionFactory;

    private static final String Resource_Ids = "order";

    @Resource
    private DataSource dataSource;

    /**
     * 客户端信息配置
     * clientId:用来表示客户端ID,必须
     * secret:客户端安全码,
     * scope:用来限制客户端的访问范围,如果是空的画,那么客户端拥有全部的访问范围
     * authrizedGrantTypes:此客户端可以使用的授权类型,默认为空
     * authorities:此客户端可以使用的权限
     * redirectUris:回调地址,授权服务回往该回调地址推送此客户端的相关的信息
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //配置两个客户端,一个用于password认证一个用于client认证(数据库版)
//        clients.jdbc(dataSource)
        //配置两个客户端,一个用于password认证一个用于client认证(内存版)
        clients.inMemory()
                //client模式
                .withClient("client_1")//clientId
                .secret(passwordEncoder.encode("client_1"))//客户端密钥
                .resourceIds(Resource_Ids)//客户端拥有的资源列表
                .authorizedGrantTypes("client_credentials", "refresh_token")
                .scopes("select")//允许的授权范围
                .authorities("oauth2")//此客户端可以使用的权限
                .and()
                //密码模式
                .withClient("client_2")
                .secret(passwordEncoder.encode("client_2"))
                .resourceIds("smeltingSteel")
                .authorizedGrantTypes("password", "refresh_token")
                .scopes("select")
                .authorities("oauth2");
    }

    @Autowired
    public UserDetailsService userDetailsService;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.userDetailsService(userDetailsService)
                .tokenStore(new RedisTokenStore(redisConnectionFactory))
                //启用oauth2管理
                .authenticationManager(authenticationManager)
                //接收GET和POST
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
        ;
    }

    //配置认证相关属性
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security
                .allowFormAuthenticationForClients()   // 允许 /oauth/token的端点表单认证
                .checkTokenAccess("permitAll()")   //公开  /oauth/checkout_token接口
                .tokenKeyAccess("permitAll()");//公开 /oauth/token_key接口
    }
}
2.2配置WebSecurityConfig
@Configuration
@EnableWebSecurity
public class MyWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;

    @Autowired
    private PersistentTokenRepository persistentTokenRepository;
    @Autowired
    private UserDetailsService userDetailsService;

    /**
     * 如果调用super.configure 默认使用父类型的默认配置
     * 默认规则是用户名必须是username  密码必须是password
     * 此逻辑由UsernamePasswordAuthenticationFilter规定
     *
     * @param zfyy
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        认证部分
        http.formLogin()
                .usernameParameter("username").passwordParameter("password");//配置前端请求的用户名和密码参数名称
        http.authorizeRequests()
                .antMatchers("/favicon.ico","/oauth/**").permitAll()
                .antMatchers("/oauth/token").permitAll()

        http.csrf().disable();

    }
    @Bean
    public PersistentTokenRepository persistentTokenRepository(DataSource dataSource) {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
//        设置启动的时候创建表格,只有数据库中不存在表d格的时候可以使用,默认值是false,第二次启动需要注释掉
//        jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }

    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
    }
}

2.3配置ResourceServerConfig.java
@Configuration
@EnableResourceServer
public class myResourceServerConfigurer extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("smeltingSteel")//资源Id
                .tokenServices(tokenServices())//使用远程服务验证令牌的服务
                .stateless(true);//无状态模式
    }
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()//校验请求
                .antMatchers("/springbootSecurity/**")//路径匹配规则
                .access("#oauth2.hasScope('select')")//需要匹配scope
                .and().csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    }

    //    配置access_token远程验证策略
    public ResourceServerTokenServices tokenServices() {
        RemoteTokenServices services = new RemoteTokenServices();
        services.setCheckTokenEndpointUrl("http://localhost:9001/oauth/check_token");
        services.setClientId("client_2");
        services.setClientSecret("client_2");
        return services;
    }
}
2.4配置数据库用户 userDetailsServiceImpl.java
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private UserInfoService userInfoService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserInfoPO userInfoPO = userInfoService.selectByUserName(username);
        if (userInfoPO == null) {
            throw new UsernameNotFoundException("用户未查询到");
        }
//        用户登录成功后   查询用户的权限集合,包括角色和权限
//        在springSecurity 对角色的命名有严格规则,要求角色名称的前缀必须是ROLE_  不要在数据库中保存
//        在springSecurity中,角色和权限平等的,都代表用户的访问权限。
        List<String> roles = userInfoService.findRolesByUser(userInfoPO);
        List<String> resources = userInfoService.findResourcesByUser(userInfoPO);
//        权限集合
        String[] authorities = new String[roles.size() + resources.size()];
        for (int i = 0; i < roles.size(); i++) {
            authorities[i] = "ROLE_" + roles.get(i);
        }
        for (int i = 0; i < resources.size(); i++) {
            authorities[i + roles.size()] = resources.get(i);
        }
        return new User(userInfoPO.getName(), userInfoPO.getPassword(), AuthorityUtils.createAuthorityList(authorities));
    }
}

3使用

3.1密码模式

在这里插入图片描述

3.2刷新token

在这里插入图片描述

3.3测试接口

在这里插入图片描述

3.4测试资源接口在这里插入图片描述
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值