nacos oaut服务地址_用户认证的例子:Spring Security oAuth2 + Spring Cloud Gateway + Nacos + Dubbo...

这个例子是商城后台项目的一部分,主要使用了oAuth2的密码模式完成用户名密码认证功能。主要流程是:使用Nacos作为注册中心,操作用户的服务user-mgr-service作为服务提供者,注册到Nacos,通过Dubbo供oAuth2调用,同时oAuth2也作为Rest服务提供者,注册到Nacos,提供用户登录/user/login服务。网关Gateway也注册到Nacos,提供统一入口,路由到oAuth2服务,完成用户认证。

(文章主要写一下实现步骤,具体代码附上的话太多了,影响阅读。github.com/toyranger/c…)

1. Spring Security oAuth2 密码模式

1.1 密码模式和授权码模式

{placeholder}

2. oAuth2实现认证服务器

2.1 创建授权服务器

ClientDetailsServiceConfigurer:通过配置的数据源,配置ClientDetailsService

AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束.

AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)

@Configuration

@EnableAuthorizationServer

public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

@Autowired

private BCryptPasswordEncoder passwordEncoder;

@Bean

@Primary

@ConfigurationProperties(prefix = "spring.datasource")

public DataSource dataSource() {

return DataSourceBuilder.create().build();

}

@Bean

public TokenStore tokenStore() {

return new JdbcTokenStore(dataSource());

}

@Bean

public ClientDetailsService jdbcClientDetailsService() {

return new JdbcClientDetailsService(dataSource());

}

/***

* 用于支持密码模式

*/

@Autowired

private AuthenticationManager authenticationManager;

@Override

public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore());

}

@Override

public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {

// 允许客户端访问 /oauth/check_token检查token

security.checkTokenAccess("isAuthenticated()").allowFormAuthenticationForClients();

}

@Override

public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

clients.withClientDetails(jdbcClientDetailsService());

}

/***

* 内存模式

*/

// @Override

// public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

//

// clients.inMemory()

// .withClient("client")

// .secret(passwordEncoder.encode("secret"))

// .authorizedGrantTypes("password", "refresh_token")

// .scopes("backend")

// .resourceIds("backend-resources")

// .accessTokenValiditySeconds(60 * 60 * 24)

// .refreshTokenValiditySeconds(60 * 60 * 24 * 30);

// }

}

复制代码

2.2 创建认证服务器和资源服务器

认证服务器的任务是根据用户名查询用户,以及用户所具有的权限,资源服务器的任务是配置访问资源(url)所需要的对应的权限。这里把他们写在一个Configuration中

@Configuration

@EnableWebSecurity

@EnableResourceServer

public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

@Bean

public BCryptPasswordEncoder passwordEncoder() {

return new BCryptPasswordEncoder();

}

@Bean

public UserDetailsService userDetailsService() {

return new UserDetailsServiceImpl();

}

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.userDetailsService(userDetailsService());

}

/***

* 用于支持 password 模式

* @return

* @throws Exception

*/

@Bean

@Override

public AuthenticationManager authenticationManagerBean() throws Exception {

return super.authenticationManagerBean();

}

@Override

public void configure(WebSecurity web) throws Exception {

web.ignoring().antMatchers("/user/login");

}

@Override

protected void configure(HttpSecurity http) throws Exception {

http.exceptionHandling().and()

.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

.and()

.authorizeRequests()

.antMatchers("/user/info").hasAnyAuthority("UserInfo")

.antMatchers("/user/logout").hasAnyAuthority("UserLogout");

}

}

复制代码

2.3 在用户认证的userDetailsService中,需要通过Dubbo调用user-mgr-service提供的服务

public class UserDetailsServiceImpl implements UserDetailsService {

@Reference(version = "1.0.0")

private UserMgrApi userMgrApi;

@Reference(version = "1.0.0")

private PermissionMgrApi permissionMgrApi;

@Override

public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

User userByName = userMgrApi.selectOne(s);

if (null == userByName) {

return null;

}

List grantedAuthorities = Lists.newArrayList();

List permissions = permissionMgrApi.selectListByUserId(userByName.getId());

permissions.forEach(permission -> {

GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getEnname());

grantedAuthorities.add(grantedAuthority);

});

return new org.springframework.security.core.userdetails.User(userByName.getUsername(),

userByName.getPassword(), grantedAuthorities);

}

}

用户信息是基于RBAC授权模型,通过username查询用户,查到用户之后通过userId查询对应的权限,都是很简单的dao操作,使用mybatis(plus)就可以完成。

复制代码

3. oAuth2对外提供rest接口,/user/login

oAuth2密码需要传username、password、grant_type、client_id、client_secret五个参数,而用户只需要传username和password即可,所以其余的参数需要登录服务自己传过去。

这里使用RestTemplate,向oAuth2服务发起请求。

成功认证之后,会得到Token。

@PostMapping("/user/login")

public CommonsResponse login(@RequestBody LoginParam loginParam) {

String tokenUrl = "http://localhost:8091/oauth/token";

MultiValueMap multiValueMap = new LinkedMultiValueMap<>();

multiValueMap.add("username", loginParam.getUsername());

multiValueMap.add("password", loginParam.getPassword());

multiValueMap.add("grant_type", oauth2_grant_type);

multiValueMap.add("client_id", oauth2_client_id);

multiValueMap.add("client_secret", oauth2_client_secret);

HttpHeaders headers = new HttpHeaders();

headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

HttpEntity> entity = new HttpEntity<>(multiValueMap, headers);

TokenEntity tokenEntity;

try {

tokenEntity = restTemplate.postForObject(tokenUrl, entity, TokenEntity.class);

} catch (Exception e) {

tokenEntity = null;

}

if (null == tokenEntity) {

return new CommonsResponse(BaseStatusEnum.UNAUTHORIZED.getIndex(),

BaseStatusEnum.UNAUTHORIZED.getMsg(), null);

}

return new CommonsResponse(BaseStatusEnum.SUCCESS.getIndex(), BaseStatusEnum.SUCCESS.getMsg(),

tokenEntity);

}

复制代码

4. 加入网关Gateway

网关可以限流和熔断,为应用提供统一的入口。这里只使用了基本的功能。

cloud:

nacos:

discovery:

server-addr: localhost:8848

gateway:

# 设置与服务注册发现组件结合,这样可以采用服务名的路由策略

discovery:

locator:

enable: true

routes:

- id: BUSINESS-OAUTH2

# 采用LoadBalanceClient方式请求,以lb://开头,后面跟注册在nacos上的服务名

uri: lb://business-security

# 断言,或者叫谓词

predicates:

- Path=/api/user/**

filters:

- StripPrefix=1

复制代码

5. 运行示例

5.1 注册到Nacos:

5.2 访问网关:

5.3 测试token权限

可以看到在资源服务中配置了

.antMatchers("/user/info").hasAnyAuthority("UserInfo"),即访问/user/info需要UserInfo权限,而此时RBAC表中,"user"用户具有这个权限

所以可以访问成功

此时如果我把"user"用户的/user/logout权限去掉(对应的表是tb_role_permission),那么他访问这个url的时候就会返回没有权限:

(好像是,修改了权限,之前获取的token就会失效,需要重新获取)

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

打赏
文章很值,打赏犒劳作者一下
表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页

打赏

秋日的前奏

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者