微服务项目之认证服务
一、Java中的权限框架
1.1 RBAC
RBAC 是基于角色的访问控制(Role-Based Access Control )在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。
RBAC 认为授权实际上是Who 、What 、How 三元组之间的关系,也就是Who 对What 进行How 的操作,也就是“主体”对“客体”的操作。
Who:是权限的拥有者或主体(如:User,Role)。
What:是操作或对象(operation,object)。
How:具体的权限(Privilege,正向授权与负向授权)。
1.2 RBAC的组成
RBAC通过定义角色的权限,并对用户授予某个角色从而来控制用户的权限,实现了用户和权限的逻辑分离,极大地方便了权限的管理
- User(用户):每个用户都有唯一的UID识别,并被授予不同的角色
- Role(角色):不同角色具有不同的权限
- Permission(权限):访问权限,粗粒度(后台系统左菜单)、细粒度(控制页面中某些功能的权限:比如新增、修改、导出、查询等)
- 用户-角色映射:用户和角色之间的映射关系
- 角色-权限映射:角色和权限之间的映射
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IuecOo7q-1617706858622)(img/001.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iKfbi8NL-1617706858626)(img/002.png)]
1.3 RBAC的安全原则
RBAC支持三个著名的安全原则:最小权限原则、责任分离原则和数据抽象原则
- 最小权限原则:RBAC可以将角色配置成其完成任务所需的最小权限集合
- 责任分离原则:可以通过调用相互独立互斥的角色来共同完成敏感的任务,例如要求一个计账员和财务管理员共同参与统一过账操作
- 数据抽象原则:可以通过权限的抽象来体现,例如财务操作用借款、存款等抽象权限,而不是使用典型的读、写、执行权限
1.4 RBAC的优缺点
(1)优点:
- 简化了用户和权限的关系
- 易扩展、易维护
(2)缺点:
- RBAC模型没有提供操作顺序的控制机制,这一缺陷使得RBAC模型很难适应哪些对操作次序有严格要求的系统
1.5 RBAC常用的框架
1.Shiro
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
2.Spring Security
Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架。
Spring Security 为基于J2EE企业应用软件提供了全面安全服务。特别是使用领先的J2EE解决方案-Spring框架开发的企业软件项目。人们使用Spring Security有很多种原因,不过通常吸引他们的是在J2EE Servlet规范或EJB规范中找不到典型企业应用场景的解决方案。
二、基于RBAC的后台系统
2.1 RBAC的后台系统
目前有很多主流的后台系统
Guns:SSM +Shiro 的后台系统
Jeecg-Boot:SpringBoot+Vue
2.2 Guns后台系统
Guns目前出了很多版本,有SSM+Shiro+BootStrap、SSM+Shiro+Layui、SpringBoot+Vue 前后端分离
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-elwGYnzB-1617706858627)(img/005.png)]
三、Spring Security
3.1 Spring Security 概述
Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架。
spring security 的核心功能主要包括:
- 认证 (你是谁)
- 授权 (你能干什么)
- 攻击防护 (防止伪造身份)
其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sIeNXLLk-1617706858629)(img/004.png)]
3.2 Spring Security核心
一、SecurityContext
安全上下文,用户通过Spring Security 的校验之后,验证信息存储在SecurityContext中
SecurityContext接口只定义了两个方法,实际上其主要作用就是获取Authentication对象
二、SecurityContextHolder
SecurityContextHolder看名知义,是一个holder,用来hold住SecurityContext实例的。在典型的web应用程序中,用户登录一次,然后由其会话ID标识。服务器缓存持续时间会话的主体信息。在Spring Security中,在请求之间存储SecurityContext的责任落在SecurityContextPersistenceFilter上,默认情况下,该上下文将上下文存储为HTTP请求之间的HttpSession属性。它会为每个请求恢复上下文SecurityContextHolder,并且最重要的是,在请求完成时清除SecurityContextHolder。SecurityContextHolder是一个类,他的功能方法都是静态的(static)。
SecurityContextHolder可以设置指定JVM策略(SecurityContext的存储策略),这个策略有三种:
MODE_THREADLOCAL:SecurityContext 存储在线程中。
MODE_INHERITABLETHREADLOCAL:SecurityContext 存储在线程中,但子线程可以获取到父线程中的 SecurityContext。
MODE_GLOBAL:SecurityContext 在所有线程中都相同。
SecurityContextHolder默认使用MODE_THREADLOCAL模式,即存储在当前线程中。
三、Authentication
authentication 直译过来是“认证”的意思,在Spring Security 中Authentication用来表示当前用户是谁,一般来讲你可以理解为authentication就是一组用户名密码信息。Authentication也是一个接口。
接口有4个get方法,分别获取
Authorities, 填充的是用户角色信息。
Credentials,直译,证书。填充的是密码。
Details ,用户信息。
Principal 直译,形容词是“主要的,最重要的”,名词是“负责人,资本,本金”。感觉很别扭,所以,还是不翻译了,直接用原词principal来表示这个概念,其填充的是用户名。
因此可以推断其实现类有这4个属性。这几个方法作用如下:
getAuthorities: 获取用户权限,一般情况下获取到的是用户的角色信息。
getCredentials: 获取证明用户认证的信息,通常情况下获取到的是密码等信息。
getDetails: 获取用户的额外信息,(这部分信息可以是我们的用户表中的信息)
getPrincipal: 获取用户身份信息,在未认证的情况下获取到的是用户名,在已认证的情况下获取到的是 UserDetails (UserDetails也是一个接口,里边的方法有getUsername,getPassword等)。
isAuthenticated: 获取当前 Authentication 是否已认证。
setAuthenticated: 设置当前 Authentication 是否已认证(true or false)。
四、UserDetails
UserDetails,看命知义,是用户信息的意思。其存储的就是用户信息
其接口方法含义如下:
getAuthorites:获取用户权限,本质上是用户的角色信息。
getPassword: 获取密码。
getUserName: 获取用户名。
isAccountNonExpired: 账户是否过期。
isAccountNonLocked: 账户是否被锁定。
isCredentialsNonExpired: 密码是否过期。
isEnabled: 账户是否可用。
五、UserDetailsService
提到了UserDetails就必须得提到UserDetailsService, UserDetailsService也是一个接口,且只有一个方法loadUserByUsername,他可以用来获取UserDetails。
通常在spring security应用中,我们会自定义一个CustomUserDetailsService来实现UserDetailsService接口,并实现其public UserDetails loadUserByUsername(final String login);方法。我们在实现loadUserByUsername方法的时候,就可以通过查询数据库(或者是缓存、或者是其他的存储形式)来获取用户信息,然后组装成一个UserDetails,(通常是一个org.springframework.security.core.userdetails.User,它继承自UserDetails) 并返回。
在实现loadUserByUsername方法的时候,如果我们通过查库没有查到相关记录,需要抛出一个异常来告诉spring security来“善后”。这个异常是org.springframework.security.core.userdetails.UsernameNotFoundException。
六、AuthenticationManager
AuthenticationManager 的作用就是校验Authentication,如果验证失败会抛出AuthenticationException异常。AuthenticationException是一个抽象类,因此代码逻辑并不能实例化一个AuthenticationException异常并抛出,实际上抛出的异常通常是其实现类,如DisabledException,LockedException,BadCredentialsException等。BadCredentialsException可能会比较常见,即密码错误的时候。
3.3 Spring Security初体验
采用SpringBoot整合Spring Security实现操作演示。
实现步骤:
1.依赖jar
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
2.编写配置文件
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { //设置过滤访问,放行资源 @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/test.do"); } //授权 拦截过滤 @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().// 开启授权登录 // antMatchers("/api/user/").//指定哪些接口,需要校验 // hasRole("admin"). //上诉接口需要具备、对应的角色才可以 anyRequest(). //其余接口 // permitAll(). //放行 运行匿名访问 authenticated(). //登录才可以访问 and().formLogin().//使用表单登录页面 loginPage("/login.html"). loginProcessingUrl("/api/user/login.do").//设置自定义的登录页面 ,如果没有自定义就是默认登录页面 passwordParameter("phone").passwordParameter("psw").//设置页面的请求参数 successHandler(new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { System.out.println("-=----->"+authentication.getName()); } }).failureHandler(new AuthenticationFailureHandler(){ @Override public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { System.err.println("-----》"+e.getMessage()); } }).permitAll().//放行表达登录相关接口 and(). httpBasic().and().csrf().disable(); } //认证 登录校验 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("admin").roles("admin"). password("$2a$10$H3wHnGELxzJHOtC/ObU.3usDypNwTDiVa0yQ9JZ0rzwjn1kiY7yZq");// 123456 } //实例化加密对象 @Bean public PasswordEncoder createPE(){ //密码的加密格式 return new BCryptPasswordEncoder(); } }
3.实现UserDetails
封装了实体接口:UserDetails
@Data @TableName("t_user") public class User implements UserDetails { @TableId(type = IdType.AUTO) private Integer id; private String username; private String password; @TableField(exist = false) private Collection<SimpleGrantedAuthority> authorities; //当前用户的 权限或者角色信息 @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } //账户是否过期 @Override public boolean isAccountNonExpired() { return true; } //账户是否锁定 @Override public boolean isAccountNonLocked() { return true; } //凭证是否过期 @Override public boolean isCredentialsNonExpired() { return true; } //当前账户是否可用 @Override public boolean isEnabled() { return true; } private int flag;//标记位 }
4.编写接口
封装了接口:UserDetailService loadUserByUsername
public interface UserService extends UserDetailsService { List<User> all(); }
@Service @Slf4j public class UserServiceImpl implements UserService { @Autowired private UserDao dao; @Override public List<User> all() { return dao.selectList(null); } @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { log.error("----->"+s); //根据传递的用户查询用户信息 User user=dao.selectOne(new QueryWrapper<User>().eq("username",s)); if(user!=null){ //查询数据库 或当前用户的角色或者权限 ArrayList<SimpleGrantedAuthority> list=new ArrayList<>(); SimpleGrantedAuthority grantedAuthority=new SimpleGrantedAuthority("admin"); list.add(grantedAuthority); user.setAuthorities(list); return user; }else { return new org.springframework.security.core.userdetails.User(s,"",null); } } }
5.启动测试
@RestController public class UserController { @Autowired private UserService service; @GetMapping("api/user/all.do") public String all(){ return "All_Users"; } @GetMapping("api/user/users.do") public List<User> users(){ return service.all(); } }
3.4 Spring Security总结
1.默认不提供外部的用户信息的时候
会有默认账号:user 密码:随机生成并显示在控制台
拦截所有
2.核心配置
WebSecurityConfigurerAdapter
重写内部的方法
3.UserDetailService 实现数据库用户信息的加载
实现接口 实现内部的方法(根据用户名查询账户信息)
四、Oauth2.0
4.1 Oauth2.0是什么
开放平台
Oauth2.0用于授权的行业标准协议。OAuth 2.0致力于简化客户端开发人员的工作,同时为Web应用程序,台式机应用程序,移动电话和客厅设备提供特定的授权流
Oauth2.0:授权
快递员:第三方 申请进入小区
居民:授权同意 第三方的进行
授权机制:数据的所有方,告诉系统,为谁授权,采用令牌(JWT)作为标记位
4.2 Oauth2.0的应用场景
对内授权:
内部有多个系统,可以实现授权式操作
对外授权:
可以让外部第三方应用,进行授权操作
各大开放平台
淘宝开放平台、天猫开放平台、钉钉开放平台、滴滴开放平台、抖音开放、头条开放、顺丰开放、小米开放、百度地图开放
4.3 Oauth2.0的模式
4.3.1 authorization-code
authorization-code:授权码模式,目前行业的标准方案
使用授权码授予类型来交换访问令牌的授权码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ESfH7C5g-1617706858631)(/img/001.PNG)]
涉及到三种角色:
1.应用程序:第三方程序系统 ,需要在开放平台的主站进行申请
2.授权程序:实现基于Oauth2.0的授权交互
3.资源程序:最终要开放的接口资源
通俗:
1.需要根据应用的ip和秘钥获取授权码
oauth/authorize?client_id=应用的ID&client_secret=应用的秘钥&response_type=code&redirect_uri=回调地址(获取授权服务返回的信息:可以获取授权码,也可以获取令牌等信息)
2.再次请求授权服务,通过授权码去获取令牌
oauth/token?client_id=应用的ID&client_secret=应用的秘钥&response_type=code&redirect_uri=回调地址&code=授权码
oauth/token?client_id=app1001&client_secret=654321&response_type=code&code=a3cepW
回调地址传递:token、refresh_token(刷新令牌 一次性)
3.有了令牌就可以操作资源
4.3.2 PKCE
PKCE RFC 7636是对授权码流的扩展,可以防止多种攻击并能够安全地从公共客户端执行OAuth交换。
它最初旨在保护移动应用程序,但是其防止授权代码注入的功能使其对每个OAuth客户端(甚至是使用客户端机密的Web应用程序)都非常有用。
主要适合于App(具备业务逻辑)
4.3.3 Client Credentials
根据请求直接获取令牌。简易
简单、不安全
4.3.4 Device Code
设备码模式,设备流中的无浏览器或受输入限制的设备使用设备代码授权类型,以将先前获得的设备代码交换为访问令牌。
4.3.5 Implicit Grant
Implicit Grant:隐式模式 目前过时
直接账号和密码获取令牌,使用令牌访问资源
4.3.6 Password Grant
Password Grant:密码模式 目前过时
直接操作账户信息,适用于都是自家系统,暴露用户的账户信息。
4.4 Oauth2.0的授权码模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a4wCOkSK-1617706858633)(/img/001.PNG)]
授权服务:要求使用Oauth2.0+Jwt+Spring Security
SpringSecurity整合Oauth2.0授权码模式
Oauth2.0选择授权码模式
实现步骤:
1.依赖jar
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-oauth2 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> <version>2.2.4.RELEASE</version> </dependency>
2.编写配置
@Configuration @EnableAuthorizationServer public class OauthConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; //实现客户端(第三方应用信息)的配置 @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory().withClient("app1001").secret("{bcrypt}654321"). //设置客户端的id和对应的秘钥 redirectUris("http://localhost:8080/api/hello.html"). autoApprove(true).scopes("all") .refreshTokenValiditySeconds(1800).accessTokenValiditySeconds(600). authorizedGrantTypes("authorization_code"); } //设置权限接口信息 @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.tokenKeyAccess("isAuthenticated()").checkTokenAccess("isAuthenticated()"). allowFormAuthenticationForClients(); } //授权服务的认证 @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager).tokenStore(createTS()); } @Bean public TokenStore createTS(){ //new RedisTokenStore() 存储到Redis //new JdbcTokenStore(); 存储到数据库 // JwtTokenStore return new InMemoryTokenStore(); //存储到内存中 } }
4.启动测试
Oauth2.0的授权码模式,默认提供的接口:
1.oauth/authorize 获取授权码
需要的参数:
client_id=应用id&response_type=code&redirect_uri=回调接口地址&forcelogin=1&state=随机签名
通过回调地址获取返回授权码
返回的内容:
code=授权码
请求接口:
http://localhost:8011/oauth/authorize?response_type=code&client_id=user-client&state=123432
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w3uRrD1J-1617706858634)(/img/o002.png)]
2.oauth/token 实现令牌的获取
需求参数:
client_id=应用id
client_secret=秘钥
code=授权码
grant_type=authorization_code
redirect_uri=获取令牌的回调地址
返回的内容:
access_token=令牌
refresh_token=刷新令牌
token_type=令牌的类型
expirse_in=失效时间
scope=权限
oauth/token?client_id=app001&redirect_uri=http://localhost:8080/html/main.html&client_secret=654321&code=nJAK9I
3.oauth/token 通过刷新令牌获取新的令牌
需要的参数:
refresh_token=刷新令牌
client_id=应用id
client_secret=秘钥
grant_type=refresh_token
返回的内容:
access_token=令牌
refresh_token=刷新令牌
token_type=令牌的类型
expirse_in=失效时间
scope=权限
4.oauth/check_token 校验令牌
5.oauth/token_key 获取JWT签名的秘钥
6.oauth/error 错误 认证失败
7.oauth/confirm_access 用户发起授权确认中
五、实现授权服务
5.1 基于Github实现登录
Github:
Client ID
e47bdea5c6527acdc526
Client Secret
dbbb3df7c8869a02d3e0ecd700f92237d7bb4f1a
Github的第三方登录:
1.申请应用
登录Github—>头像---->setting----->Developer Setting----->Oauth App ----->注册应用----->填写信息------>注册成功之后显示 Client ID和Client Secret
Client ID
9dcd96d16b6566c65436
Client Secret
02d2241d66146ab06ea9b9080df69392a2b5f3bf
client_id=9dcd96d16b6566c65436&client_secret=02d2241d66146ab06ea9b9080df69392a2b5f3bf&code
2.使用GitHub进行登录
1.在页面上添加GitHub的图标
设置点击事件
请求:https://github.com/login/oauth/authorize
需要参数:
var params="?client_id=9dcd96d16b6566c65436&scope=user&state="+(new Date()).valueOf();
名称 | 类型 | 描述 |
---|---|---|
client_id | string | 必填项。注册时从GitHub收到的客户端ID 。 |
redirect_uri | string | 授权后将用户发送到应用程序中的URL。请参阅以下有关重定向网址的详细信息。 |
login | string | 建议用于登录和授权应用程序的特定帐户。 |
scope | string | 以空格分隔的范围列表。如果未提供,则scope 对于未授权该应用程序任何作用域的用户,默认为空列表。对于拥有该应用程序授权范围的用户,不会向用户显示带有范围列表的OAuth授权页面。取而代之的是,流程的这一步将自动完成用户已为应用程序授权的一组作用域。例如,如果用户已经执行了两次Web流程并授权了一个具有user 范围的令牌和另一个具有repo 范围的令牌,则不提供的第三个Web流程scope 将收到具有user 和repo 范围的令牌。 |
state | string | 不可猜测的随机字符串。它用于防止跨站点请求伪造攻击。 |
allow_signup | string | 在OAuth流程期间,是否向未认证的用户提供注册GitHub的选项。默认值为true 。false 当策略禁止注册时使用。 |
示例代码:
$("#btngithub").click(function(){ var params="?client_id=9dcd96d16b6566c65436&scope=user&state="+(new Date()).valueOf(); location.href="https://github.com/login/oauth/authorize"+params; })
2.获取授权码,进行令牌获取
请求的接口:https://github.com/login/oauth/access_token
名称 | 类型 | 描述 |
---|---|---|
client_id | string | **需要。**您从GitHub收到的GitHub App的客户端ID。 |
client_secret | string | **需要。**您从GitHub收到的GitHub App的客户密码。 |
code | string | **需要。**您收到的作为对步骤1的响应的代码。 |
redirect_uri | string | 授权后将用户发送到应用程序中的URL。 |
state | string | 您在步骤1中提供的无法猜测的随机字符串 |
示例代码:
$("#btntoken").click(function(){ var params="client_id=9dcd96d16b6566c65436&client_secret=02d2241d66146ab06ea9b9080df69392a2b5f3bf&code="+$("#gcode").val() $.ajax({ url:"https://github.com/login/oauth/access_token", data: params, method:"post", headers:{ "Accept":"application/json" }, success:function(res){ console.log(res); } }); })
3.请求用户信息
请求接口:https://api.github.com/user?access_token=ae9218dd36eac1048130ead179e3e89182e31bbc
需要参数:access_token=上一步获取的令牌值
$("#btnuser").click(function(){
$.ajax({
url:"https://api.github.com/user?access_token=ae9218dd36eac1048130ead179e3e89182e31bbc",
method:"get",
success:function(res){
console.log(res);
}
});
})
5.2 编码实现Oauth2.0授权
Oauth2.0模式:授权码的模式
请求需求的内容:
1.获取授权码
code
2.获取令牌
access_token
3.刷新令牌获取新令牌
refresh_token
4.校验令牌
check_token
代码示例:
1.依赖jar
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2.实现配置
1.实现权限的配置
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { //密文:SHA-256+随机盐+Key 生成的单向加密 @Bean public PasswordEncoder passwordEncoder() { // NoOpPasswordEncoder return new BCryptPasswordEncoder(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } /** * 允许匿名访问所有接口 主要是 oauth 接口 * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/**").permitAll(). and().httpBasic(). and().csrf().disable(); } }
2.实现令牌存储配置
@Configuration public class RedisTokenStoreConfig { //存储到Redis ,使用的是Spring Data Redis @Autowired private RedisConnectionFactory redisConnectionFactory; @Bean public TokenStore redisTokenStore (){ return new RedisTokenStore(redisConnectionFactory); } }
3.实现UserDetail的配置
@Slf4j @Service public class LxUserDetailsService implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; //默认提供的用户信息:admin 123456 @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { log.info("username---:" + username); // 查询数据库操作 if(!username.equals("admin")){ throw new UsernameNotFoundException("the user is not found"); }else{ // 用户角色也应在数据库中获取 String role = "ROLE_ADMIN"; List<SimpleGrantedAuthority> authorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority(role)); // 线上环境应该通过用户名查询数据库获取加密后的密码 String password = passwordEncoder.encode("123456"); return new org.springframework.security.core.userdetails.User(username,password, authorities); } } }
4.实现授权配置
@Configuration @EnableAuthorizationServer public class OAuth2Config extends AuthorizationServerConfigurerAdapter { @Autowired public PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Autowired public UserDetailsService detailsService; @Autowired private TokenStore redisTokenStore; @Override public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception { /** * redis token 方式 */ endpoints.authenticationManager(authenticationManager) .userDetailsService(detailsService) .tokenStore(redisTokenStore); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { //操作数据库 --来自数据库 //clients.withClientDetails(); //来自内存 clients.inMemory() .withClient("app001") .secret(passwordEncoder.encode("123456")) .authorizedGrantTypes("refresh_token", "authorization_code") .accessTokenValiditySeconds(3600). redirectUris("http://localhost:8011/call.html") .scopes("all") .and() .withClient("app002") .secret(passwordEncoder.encode("123456")) .authorizedGrantTypes("refresh_token", "authorization_code") .accessTokenValiditySeconds(3600). redirectUris("http://localhost:8011/call.html") .scopes("all"); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.allowFormAuthenticationForClients();//允许表单提交 security.checkTokenAccess("permitAll()");//校验令牌是否有效 security.tokenKeyAccess("isAuthenticated()"); } }
3.实现系统配置文件
server: port: 8011 spring: application: name: oauthserver #服务名称 redis: database: 0 host: 39.105.189.141 port: 6380 password: qfjava
4.启动测试
访问Oauth开放的接口
测试授权:
1.请求接口
http://localhost:8011/oauth/authorize?response_type=code&client_id=app001&state=123432
调用登录页面,输入账号信息,成功之后,将授权码回调到指定的页面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GUN8cpMc-1617706858635)(/img/002.png)]
取到对应的授权码:code=u8cCJ0
2.postman进行接口请求 获取令牌
http://localhost:8011/oauth/token
需求的参数:
请求示例:http://localhost:8011/oauth/token?client_id=app001&client_secret=123456&code=u8cCJ0&grant_type=authorization_code post请求
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bgm4asJM-1617706858635)(/img/003.png)]
3.校验令牌是否有效
http://localhost:8011/oauth/check_token
需求参数:
token:令牌
请求示例:http://localhost:8011/oauth/check_token?token=0cf70c84-47c5-4132-be3d-e3e9a3f9b889
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G8sWGZmG-1617706858636)(/img/004.png)]
4.刷新令牌获取新令牌
http://localhost:8011/oauth/token post
需求参数:
client_id:id
client_secret:秘钥
refresh_token:刷新令牌
grant_type:refresh_token
请求示例:http://localhost:8011/oauth/token?client_id=app001&client_secret=123456&refresh_token=e11f035f-d6da-44ae-8529-182fb1870f4f&grant_type=refresh_token
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lP7CK4b7-1617706858637)(/img/005.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pvVOY4yh-1617706858637)(img/006.png)]
将授权码回调到指定的页面
[外链图片转存中…(img-GUN8cpMc-1617706858635)]
取到对应的授权码:code=u8cCJ0
2.postman进行接口请求 获取令牌
http://localhost:8011/oauth/token
需求的参数:
请求示例:http://localhost:8011/oauth/token?client_id=app001&client_secret=123456&code=u8cCJ0&grant_type=authorization_code post请求
[外链图片转存中…(img-Bgm4asJM-1617706858635)]
3.校验令牌是否有效
http://localhost:8011/oauth/check_token
需求参数:
token:令牌
请求示例:http://localhost:8011/oauth/check_token?token=0cf70c84-47c5-4132-be3d-e3e9a3f9b889
[外链图片转存中…(img-G8sWGZmG-1617706858636)]
4.刷新令牌获取新令牌
http://localhost:8011/oauth/token post
需求参数:
client_id:id
client_secret:秘钥
refresh_token:刷新令牌
grant_type:refresh_token
请求示例:http://localhost:8011/oauth/token?client_id=app001&client_secret=123456&refresh_token=e11f035f-d6da-44ae-8529-182fb1870f4f&grant_type=refresh_token
[外链图片转存中…(img-lP7CK4b7-1617706858637)]
[外链图片转存中…(img-pvVOY4yh-1617706858637)]