0
点赞
收藏
分享

微信扫一扫

Spring Security Oauth2架构学习

三千筱夜 2022-04-16 阅读 64

1.基本概念

简介,Oauth协议为用户资源的授权提供了一个安全的,开放而又简易的标准,同时,任何第三方都可以使用Oauth认证服务,目前Oauth是2.0版本使用最为广泛.

分析一下网站使用vx认证的过程:

1.首先用户想访问资源,需要认证,使用第三方认证比如(vx,qq,新浪等等)

2.用户确认使用第三方认证,那么需要向对应的第三方请求授权码,拿到授权码后,再用拿到的授权码去授权服务器请求令牌

3.服务端校验令牌,如果成功则授予相应的权限访问资源,获取对应的资源或者个人信息.

 Oauth2这个是为了方便安全的用户(第三方)登录用的。
一开始听见oauth2这个词肯定是很懵的。这是啥,鉴权用的?认证用的?授权用的?跟shiro(java)是一个东西吗?

其实oauth就是一个流程。我们根据这个流程的要求写代码、

oauth有一个授权服务器。是作为用户的认证用的。对于服务端来说只需实现一个oauth的授权服务器。对于用户来说(调用授权认证的研发)只需根据流程发请求就可以了。

Oauth 有四种实体。 下面以用QQ登陆微博为例。
资源所有者(resource owner) 我们(普通用户)

资源服务器(resource server) QQ的后台服务器(获取账号,昵称,头像等)。

应用程序(client) 微博这个平台

授权服务器(Authorization Server) 认证QQ & 授权用的。

oauth有四种方式。 目的是不把密码暴露给第三方。(即不把QQ账号密码暴露给微博)
1.授权码 authorization code 也是安全等级最高的一版。 支持refresh token    一般都用这个,微博QQ登录就是这种方式。

2.密码 password credentials 支持refresh token    这种安全等级低。谁知道client会不会偷摸存你密码不。一般内部使用

3.简化模式 implicit 不支持refresh token   这种没有获取code的步骤。请求就给token、 没get到这个的优势

4.客户端模式 client credentials 不支持refresh token   这种是被信任的内部client使用。一般内部平台间使用

注: client_id   client_secret 是用来oauth server鉴别client用的。

下面详细说明一下各个方式。简化模式就不说了。
 

1.授权码模式

授权码模式就是通过在授权服务器获得验证码和令牌双重校验,然后进行授权访问.

1)添加依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>spring-sercrity-student</groupId>
    <artifactId>spring-sercrity-myself</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <spring-cloud.version>2021.0.1</spring-cloud.version>
    </properties>



    <dependencyManagement>
        <dependencies>
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- 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.5.RELEASE</version>
        </dependency>

        <!--当oauth2项目启动出现无法读取的异常添加此依赖-->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>


        <!--spring cloud整合security框架-->
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-security -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
            <version>2.2.4.RELEASE</version>
        </dependency>


        <!-- web模块 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>


    </dependencies>


</project>

加入SpringCloud依赖,而SpringCloud也整合了security和oauth2框架.



2)创建securityconfig:

    /**
     * 授权框架配置类
     */
    @Configuration
    //表示启动webSecurity
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                    .authorizeRequests()
                   //放行oauth认证,登录认证,用户退出
                    .antMatchers("/oauth/**", "/login/**", "logout/**").permitAll()
    
                    //拦截未被放行的所有请求
                    .anyRequest().authenticated()
                    .and()
    
                    //表单认证全部放行
                    .formLogin().permitAll();
        }
    
        //返回一个加密器
        @Bean
        public PasswordEncoder getPw() {
            return new BCryptPasswordEncoder();
        }
    }

3)自定义用户信息包装类

/**
 * 用户信息包装类
 */
public class User implements UserDetails {

    //用户名
    private String userName;

    //用户密码
    private String password;

    //用户权限
    private List<GrantedAuthority> authorities;

    public User(String userName, String password, List<GrantedAuthority> authorities) {
        this.userName = userName;
        this.password = password;
        this.authorities = 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;
    }
}

4)自定义认证类

/**
 * 自定义认证
 */
@Service
public class UserService implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        String encodePw = passwordEncoder.encode("123");

     return new User(username,encodePw, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}

这里只是演示,实际密码应该从数据库中查询,进行校验.

5)配置授权服务器

/**
 * 授权服务器配置
 *
 * @author 秦杨
 * @version 1.0
 */
@Configuration
//表示启用授权服务器配置
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //这里只是暂时写死,正常需要去授权服务器注册
                //配置Client id
                .withClient("admin")

                //配置Client security(密钥)
                .secret(passwordEncoder.encode("Vermouth2022"))

                //设置Token失效时间
                .accessTokenValiditySeconds(3600)

                //重定向地址,用于授权成功后跳转
                .redirectUris("http://www.baidu.com")
                //授权范围,申请读取哪部分内容
                .scopes("all")
                //授权类型
                .authorizedGrantTypes("authorization_code");
    }
}

这里拿到的客户端id和密钥将在第二次令牌验证的时候同时进行验证!!!

6)配置资源服务器

/**
 * 资源服务配置
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .requestMatchers()

                //放行需要获取的对应的资源
                .antMatchers("/user/**");
    }


}
    

当token令牌验证成功后就放行需要的功能.

具体操作流程:

这只是模拟授权码登录,实际上一般都是我们去vx这种第三方获取权限,所以也是访问他们的授权服务器和资源管理器,而这是为了了解流程.

当用户需要第三方授权时,我们会使用oauth协议,同时让用户扫描或者登录或者人脸识别等等来认证用户身份,同时将这些认证的信息发送到授权服务器.

授权请求:http://localhost:8080/oauth/authorize?response_type=code&client_id=admin&redirect_uri=http://www.baidu.com&scope=all

这里url的信息必须跟授权服务器中定义的一致,否则会报错.

/oauth/是我们授权框架的接口

/authorize表示要进行授权,请求一个验证码,客户端id是admin,成功后跳转到百度,请求的权限范围是all.

当我们访问后,它会自动跳转到login界面,让我们进行认证

 

输入账号密码/扫码后,成功后就会成功进入到我们授权的页面里

 

Approve表示同意授权,Dent表示拒绝.

同意后我们就跳转到了指定的页面.

 仔细看,页面的url中带了一个code,而其值就是我们授权服务器返回的一个验证码.

拿到验证码我们要再去授权服务器进行验证

要用post请求去发送信息,我们用postman去使用.

 使用验证码认证,同时输入我们在授权服务器中写死的用户端id,这个正常情况应该要注册的,为了演示就写死了.

同时请求体中还要带上授权服务器需要的参数和返回的验证码.

 点击发送后我们拿到一个授权服务器返回的token和一些信息.

{
    "access_token": "5afbda45-62aa-4345-bd20-a4e647356cf2",
    "token_type": "bearer",
    "expires_in": 3599,
    "scope": "all"
}

拿到了token之后,就可以去访问我们需要的资源了,带上这个token去进行请求

 提交后就会访问我们需要的功能,然后拿到相应的资源,这就是授权码模式

举报

相关推荐

0 条评论