spring-cloud-starter-oauth2搭建授权中心服务

A 抖音APP
B 微信APP
C 微信授权中心

1授权码(authorization-code):
A前端跳转C授权地址,授权中心唤醒B登录后授权,跳转指定的重定向地址(一般就是A)携带一个code,A后端再用client_secret、code获取token 最最主流的应用模式

a.
http://127.0.0.1:8081/oauth/authorize?response_type=code&client_id=test&redirect_uri=http://127.0.0.1:8082/test&scope=read
b.
http://127.0.0.1:8082/test?code=drQ9y6
c.
http://127.0.0.1:8081/oauth/token?client_id=test&client_secret=test&grant_type=authorization_code&code=CGIFwy&redirect_uri=http://127.0.0.1:8082/test

 

 

 

 

 


2隐藏式(implicit):
A前端跳转C授权地址,授权中心唤醒B登录后授权,直接跳转指定的重定向地址(一般就是A) #号携带token 适合纯前端应用

http://127.0.0.1:8081/oauth/authorize?response_type=token&client_id=test&redirect_uri=http://127.0.0.1:8082/test&scope=read

3密码式(password):
A前端直接要求用户把登录账号密码填充,然后请求C拿到token 非常不安全,适合高可信任的三方比如微服务网关zuul-gataway等
http://127.0.0.1:8081/oauth/token?grant_type=password&username=admin&password=admin&client_id=test&client_secret=test


4.客户端凭证(client credentials):
明显可以看出整个过程不涉及用户信息,这种方式给出的令牌,是针对第三方应用的,而不是针对用户的,即有可能多个用户共享同一个令牌。
http://127.0.0.1:8081/oauth/token?grant_type=client_credentials&client_id=test&client_secret=test

 


实测tips

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
较新的版本需要注意授权服务器需要配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.requestMatchers().anyRequest()
.and()
.authorizeRequests()
.antMatchers("/oauth/**","/oauth/authorize").authenticated();
http.formLogin();
} authenticated 而不是 permitAll
否则提示
{"code":500,"msg":"User must be authenticated with Spring Security before authorization can be completed.","data":null,"timestamp":1603527083994}

同时,假如要在授权服务上做资源服务配置,
@EnableWebSecurity
@Order(2)

@EnableResourceServer
@Order(6)
即前者初始化要优先
否则提示
<oauth>
<error_description>Full authentication is required to access this resource</error_description>
<error>unauthorized</error>
</oauth>

参考文档
http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html
https://blog.csdn.net/qq_34997906/article/details/89609297

后续测试方向

1.微服务整合,资源服务配置,打算网关就做纯网关路由,授权中心不和用户资源服务做关联,甚至可以不注册到eureka

2.权限范围测试及各个模式异同

2020-10-28

完成微服务整合以后我们在资源服务上面开启安全注解,这样我们就能愉快的使用注解来细颗粒的控制权限了

@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)

一、JSR-250注解
  @DenyAll 拒绝所有访问
  @RolesAllowed({"USER", "ADMIN"})  该方法只要具有"USER", "ADMIN"任意一种权限就可以访问。这里可以省 略前缀ROLE_,实际的权限可能是ROLE_ADMIN
注意

客户端方式 client authorities以client表的authorities字段为准

授权码、隐藏式、密码登陆模式  authorities以user对应的role表为准

 
  @PermitAll 允许所有访问
 
 
二、prePostEnabled注解
  @PreAuthorize:在方法执行之前执行,而且这里可以调用方法的参数,也可以得到参数值,这是利用JAVA8的参数名反射特性,如果没用JAVA8,那么也可以利用Spring Security的@P标注参数,或者Spring Data的@Param标注参数。
@PreAuthorize("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)")
void changePassword(@P("userId") long userId ){  }
这里表示在changePassword方法执行之前,判断方法参数userId的值是否等于principal中保存的当前用户的userId,或者当前用户是否具有ROLE_ADMIN权限,两种符合其一,就可以访问该方法。
  @PostAuthorize:在方法执行之后执行,而且这里可以调用方法的返回值,如果EL为false,那么该方法也已经执行完了,可能会回滚。EL变量returnObject表示返回的对象。
@PostAuthorize
User getUser("returnObject.userId == authentication.principal.userId or hasPermission(returnObject, 'ADMIN')");
  @PreFilter:在方法执行之前执行,而且这里可以调用方法的参数,然后对参数值进行过滤或处理或修改,EL变量filterObject表示参数,如有多个参数,使用filterTarget注解参数。只有方法参数是集合或数组才行。(很少会用到,与分页技术不兼容)

三、securedEnabled 注解
@GetMapping("/helloUser")
@Secured({"ROLE_normal","ROLE_admin"})
public String helloUser() {
    return "hello,user";
}

说明:拥有normal或者admin角色的用户都可以方法helloUser()方法。另外需要注意的是这里匹配的字符串需要添加前缀“ROLE_“如果我们要求,只有同时拥有admin & noremal的用户才能方法helloUser()方法,这时候@Secured就无能为力了。

 
参考 
 
 
还有一类根据client申请token时声明申请的scope范围(必须在分配可访问的范围内选择)来控制权限。
比如我允许client有read write update create

 我在申请token的时候指定read update

此时如果资源服务器的scope要求write或者create的scope权限就无权访问了。

  

 
总的来说有两类权限控制方式role/authorities(细颗粒,看user有什么role当客户端模式的时候看client有什么authorities)  scope(粗颗粒看client申请了什么scope)
 
 

3.自定义登录授权

 增2020-11-03

经过实践查阅认证自定义登录的概念有两类,第一种是针对Oauth四种模式的扩展,即获取token的扩展,第二种是针对授权码模式和隐藏模式在跳转认证服务时登录认证时的自定义扩展,即spring security的登录扩展。两种都能集成短信、社交、扫码、用户密码的扩展,前者对调用者来说每次都要改调用方式,而后者授权流程不变,只是在认证服务的登录页面提供多元登录操作,只需要服务方去集成,调用者不需要做任何调整。说的有点抽象哈,请看下图。

a.纵向扩展比较简单,继承AbstractTokenGranter重写getOAuth2Authentication方法,然后将新的授权方法塞入。

 效果如下

b.横向扩展,需要自定义登陆页面和授权页面(后期接入许多认证方式都要加对应前端的东西),指定spring security的登陆页面地址和UsernamePasswordAuthenticationFilter的目标过滤地址,如果新增扩展自定义的过滤器需要注释否则会走再经过上面提到spring security的UsernamePasswordAuthenticationFilter。还要再指定经过过滤器以后跳转的授权页地址替换默认的“oauth/comfirm_access”。

    

 

 然后校验的核心就是MyUsernamePasswordAuthenticationFilter自定义过滤器和MyAuthenticationManager自定义的认证类,然后加入配置

 这样授权码和隐藏模式在登陆的时候可以自定义我们的规则,成功后点击授权就可以发放code或者token了。

2021-01-29

尝试将oauth服务接入eureka集群,gateway过滤认证且允许传递header。oauth自定义接口测试正常。前端通过gateway顺利获取到token。

再尝试经过dateway授权码模式获取code时发现域名地址出现了变化。这里就涉及到eureka注册服务的ip和主机名策略。

eureka:
instance:
prefer-ip-address: true
ip-address: localhost #为了让授权码模式跳转在同一个域名 配置成和gateway同入口的域名
因为新老服务的关系,eureka client注册存在差异。老版本默认ip注册,新版本默认主机名注册。导致gateway ip域名请求时的session在oauth主机名下跳转登陆页面获取不到session
导致报错404.把oauth的实例改成和gateway同一个域名ip可解决。
原文地址:https://www.cnblogs.com/xuetieqi/p/13869947.html