微服务项目之认证服务

微服务项目之认证服务

一、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,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

http://shiro.apache.org/

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 前后端分离

https://www.stylefeng.cn/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-elwGYnzB-1617706858627)(img/005.png)]

三、Spring Security

3.1 Spring Security 概述

Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架。

spring security 的核心功能主要包括:

  • 认证 (你是谁)
  • 授权 (你能干什么)
  • 攻击防护 (防止伪造身份)

其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式。

https://www.springcloud.cc/spring-security-zhcn.html

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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_idstring必填项注册时从GitHub收到的客户端ID 。
redirect_uristring授权后将用户发送到应用程序中的URL。请参阅以下有关重定向网址的详细信息。
loginstring建议用于登录和授权应用程序的特定帐户。
scopestring以空格分隔的范围列表。如果未提供,则scope对于未授权该应用程序任何作用域的用户,默认为空列表。对于拥有该应用程序授权范围的用户,不会向用户显示带有范围列表的OAuth授权页面。取而代之的是,流程的这一步将自动完成用户已为应用程序授权的一组作用域。例如,如果用户已经执行了两次Web流程并授权了一个具有user范围的令牌和另一个具有repo范围的令牌,则不提供的第三个Web流程scope将收到具有userrepo范围的令牌。
statestring不可猜测的随机字符串。它用于防止跨站点请求伪造攻击。
allow_signupstring在OAuth流程期间,是否向未认证的用户提供注册GitHub的选项。默认值为truefalse当策略禁止注册时使用。

示例代码:

$("#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_idstring**需要。**您从GitHub收到的GitHub App的客户端ID。
client_secretstring**需要。**您从GitHub收到的GitHub App的客户密码。
codestring**需要。**您收到的作为对步骤1的响应的代码。
redirect_uristring授权后将用户发送到应用程序中的URL。
statestring您在步骤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)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值