SpringSecurity系列文章 (六)Spring Security Oauth2

Spring Security Oauth2

授权服务器

BUFxOO.png

  • Authorize Endpoint : 授权端点,进行授权
  • Token endpoint :令牌端点,进行授权拿到对应的token
  • Introspection Endpoint :校验端点,校验token
  • Revocation Endpoint : 撤销端点,撤销授权

Spring Security Oauth2

BUAdr8.png

流程:

1.用户访问,此时,没有token , Oauth2RestTemplate会出错,这个报错信息将会被Oauth2ClientContextFilter 捕获并重定向到认证服务器。

2.认证服务器通过Authorization endpoint 进行授权,并通过AuthhorizationServerTokenServices生成授权码并返回给客户端

3.客户端拿到授权码去认证服务器通过Token endpoint 调用AuthorizationServerTokenServices生成Token并返回给客户端

4.客户端拿到Token 去资源服务器访问资源,一般会通过Oauth2AuthenticationManager调用ResourceServerTokenServices进行校验,校验通过后可以获取到资源

授权码模式:

效果是访问http://localhost:8080/oauth/authorize?response_type=code&client_id=admin&redirect_uti=http://www.baidu.com 跳到百度页面并且带授权码,并获取token

先看效果图

在这里插入图片描述

在这里插入图片描述

通过postman获取token:
在这里插入图片描述

在这里插入图片描述

通过token获取到资源,比如获取当前的用户。

BaSwjg.png

当我们把令牌给修改了就会报错。未认证。

看代码:

pom.xml
<?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 https://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.3.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.kaysanshi</groupId>
    <artifactId>spring-security-oauth2-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-security-oauth2-demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR2</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

java代码:
/**
 * 使用授权码模式
 */
@SpringBootApplication
public class SpringSecurityOauth2DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringSecurityOauth2DemoApplication.class, args);
    }

}

 配置实现类         /

/**
 * Description:
 * spring security配置
 * WebSecurityConfigurerAdapter是默认情况下 Spring security的http配置
 * 优先级高于ResourceServerConfigurer,用于保护oauth相关的endpoints,同时主要作用于用户的登录(form login,Basic auth)
 *@EnableWebSecurity :表示启动webSecurity
 * @date:2020/10/29 9:41
 * @author: kaysanshi
 **/
@Configuration
@EnableWebSecurity
public class SecurityConfigure extends WebSecurityConfigurerAdapter {

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 进行
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/oauth/**", "/login/**", "logout/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .permitAll();
    }
}
/

/**
 * Description: 授权服务器的配置
 * author2配置
 * AuthorizationServerConfigurerAdapter 包括:
 * ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
 * AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束.
 * AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。
 *
 * @date:2020/10/29 9:30
 * @author: kaysanshi
 **/
@Configuration
@EnableAuthorizationServer // 开启认证授权服务器
public class AuthorizationServerConfigure extends AuthorizationServerConfigurerAdapter {

    /**
     * 配置密码加密,因为再UserDetailsService是依赖与这个类的。
     *
     * @return
     */
    // 指定密码的加密方式
    @Autowired
    private PasswordEncoder passwordEncode;

    /**
     * ClientDetailsServiceConfigurer
     * 主要是注入ClientDetailsService实例对象(唯一配置注入)。其它地方可以通过ClientDetailsServiceConfigurer调用开发配置的ClientDetailsService。
     * 系统提供的二个ClientDetailsService实现类:JdbcClientDetailsService、InMemoryClientDetailsService。
     * 演示提供了用户名和密码
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients)
            throws Exception {
        // 配置一个客户端用于password认证的。同时也可以同时配置两个,可以再配置一个基于client认证的。
        clients.inMemory()
                // 配置clientId
                .withClient("admin")
                // 配置client-secret
                .secret(passwordEncode.encode("112233"))
                // 配置token过期时间
                .accessTokenValiditySeconds(2630)
                // 配置 redirectUri,用于授权成功后跳转
                .redirectUris("http://www.baidu.com")
                // 配置申请的权限范围
                .scopes("all")
                // 配置grant_type 表示授权类型。 使用授权码模式
                .authorizedGrantTypes("authorization_code");

    }
}
/
/**
 * Description:
 * 配置资源服务器 : ResourceServerConfigurerAdapter
 * ResourceServerConfigurerAdapter是默认情况下spring security oauth 的http配置。
 *
 * @date:2020/10/29 9:44
 * @author: kaysanshi
 **/
@Configuration
@EnableResourceServer
public class ResourceServerConfigure extends ResourceServerConfigurerAdapter {
    /**
     * 配置响应资源的访问。
     *
     * @param http
     * @throws Exception
     */
    // 配置 URL 访问权限
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .requestMatchers()
                .antMatchers("/test/**");
    }
}

//////////// user类  ///////////////////////
////////////////////////////////////////////
/**
 * 自定义User 实现时一定要返回true 否则不能进行认证
 * @Author kay三石
 * @date:2020/10/31
 */
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 null;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }
	// 自动生成时是返回的为false,会导致不能进入
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
	// 自动生成时是返回的为false,会导致不能进入

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
	// 自动生成时是返回的为false,会导致不能进入

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }	
	// 自动生成时是返回的为false,会导致不能进入
    @Override
    public boolean isEnabled() {
        return true;
    }
}

 userService实现类  /

/**
 * @Author kay三石
 * @date:2020/10/31
 */
@Service
public class UserService implements UserDetailsService {
    /**
     * 注入刚引入的bean
     */
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        String password =passwordEncoder.encode("123");
        return new User("admin",password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}

 controller  ///

/**
 * Description:
 *  加入了author2的认证,所以要加入token访问资源时
 * @date:2020/10/29 9:46
 * @author: kaysanshi
 **/
@RestController
@RequestMapping("/test")
public class TestController {
    /**
     * 获取当前用户
     * @return
     */
    @GetMapping("/getCurrentUser")
    public Object getCurrentUser(Authentication authentication){
        return authentication.getPrincipal();
    }
}



遇到问题,但是后台并未报错,只需要把这个给修改即可 : 用户已失效,用户账户已经锁定

BUjSMj.png

解决:明明用户名和密码正确,而且没有设置状态锁定,怎么被锁定了呢?这是由于我们在重写UserDetails接口时,有个默认实现的方法public boolean isAccountNonLocked(),默认返回的是false

 @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

密码模式

使用密码模式获取资源:

看效果:

获取token

BaiSQf.png

通过token获取信息

Baipy8.png

pom.xml
<?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 https://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.3.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.kaysanshi</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-security-oauth2</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR2</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

java 代码

由于这个只是配置类中不太一样,这里我只展现出配置类的代码:

/**
 * Description:
 * author2配置
 * AuthorizationServerConfigurerAdapter 包括:
 * ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
 * AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束.
 * AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。
 *
 * @date:2020/10/29 9:30
 * @author: kaysanshi
 **/
@Configuration
@EnableAuthorizationServer // 开启认证授权服务器
public class AuthorizationServerConfigure extends AuthorizationServerConfigurerAdapter {

    // 密码授权的操作就是通过这个对象把密码传入授权服务器的
    @Autowired
    private AuthenticationManager authenticationManager;

    // 将令牌信息存储到内存中
    @Autowired(required = false)
    private TokenStore inMemoryTokenStore;

    // 该对象将为刷新token提供支持
    @Autowired
    private UserService userService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * ClientDetailsServiceConfigurer
     * 主要是注入ClientDetailsService实例对象(唯一配置注入)。其它地方可以通过ClientDetailsServiceConfigurer调用开发配置的ClientDetailsService。
     * 系统提供的二个ClientDetailsService实现类:JdbcClientDetailsService、InMemoryClientDetailsService。
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients)
            throws Exception {
        // 配置一个基于password认证的。
        clients.inMemory()
                // 配置clientId
                .withClient("admin")
                // 配置client-secret
                .secret(passwordEncoder.encode("112233"))
                // 配置token过期时间
                .accessTokenValiditySeconds(2630)
                // 配置 redirectUri,用于授权成功后跳转
                .redirectUris("http://www.baidu.com")
                // 配置申请的权限范围
                .scopes("all")
                // 配置grant_type 表示授权类型。 使用密码模式
                .authorizedGrantTypes("password");
    }

    /**
     * 使用密码模式所需配置
     * AuthorizationServerEndpointsConfigurer 访问端点配置 是一个装载类
     * 装载Endpoints所有相关的类配置(AuthorizationServer、TokenServices、TokenStore、ClientDetailsService、UserDetailsService)。
     * @param endpoints
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.tokenStore(inMemoryTokenStore) //配置令牌的存储(这里存放在内存中)
                .authenticationManager(authenticationManager)
                .userDetailsService(userService);
    }
}
///
///                                         ///

/**
 * Description:
 * spring security配置
 * WebSecurityConfigurerAdapter是默认情况下 Spring security的http配置
 * 优先级高于ResourceServerConfigurer,用于保护oauth相关的endpoints,同时主要作用于用户的登录(form login,Basic auth)
 *
 * @date:2020/10/29 9:41
 * @author: kaysanshi
 **/
@Configuration
@EnableWebSecurity
public class SecurityConfigure extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        // 使用BCrypt强哈希函数加密方案(密钥迭代次数默认为10)
        return new BCryptPasswordEncoder();
    }

    /**
     * 为了让认证配置类注入使用
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/oauth/**", "/login/**", "logout/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .permitAll();
    }
}

将token存到redis

/**
 * Description:
 * 配置把token存到redis 中的redis链接
 * @date:2020/10/29 15:14
 * @author: kaysanshi
 **/
@Configuration
public class TokenStoreConfigure {

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Bean
    public TokenStore redisTokenStore(){
        return new RedisTokenStore(redisConnectionFactory);
    }
}

在授权服务器中使用redis进行存储

package com.kaysanshi.springsecurityoauth2.configure;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;

/**
 * Description: 授权服务器的配置
 * author2配置
 * AuthorizationServerConfigurerAdapter 包括:
 * ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
 * AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束.
 * AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。
 *
 * @date:2020/10/29 9:30
 * @author: kaysanshi
 **/
@Configuration
@EnableAuthorizationServer // 开启认证授权服务器
public class AuthorizationServerConfigure extends AuthorizationServerConfigurerAdapter {

    // 该对象用来支持 password 模式
    @Autowired
    AuthenticationManager authenticationManager;

    // 将令牌信息存储redis中
    @Autowired
    TokenStore redisToken;

    // 该对象将为刷新token提供支持
    @Autowired
    UserDetailsService userDetailsService;

    /**
     * 配置密码加密,因为再UserDetailsService是依赖与这个类的。
     *
     * @return
     */
    // 指定密码的加密方式
    @Bean
    PasswordEncoder passwordEncoder() {
        // 使用BCrypt强哈希函数加密方案(密钥迭代次数默认为10)
        return new BCryptPasswordEncoder();
    }

    /**
     * ClientDetailsServiceConfigurer
     * 主要是注入ClientDetailsService实例对象(唯一配置注入)。其它地方可以通过ClientDetailsServiceConfigurer调用开发配置的ClientDetailsService。
     * 系统提供的二个ClientDetailsService实现类:JdbcClientDetailsService、InMemoryClientDetailsService。
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients)
            throws Exception {
        // 配置一个客户端用于password认证的。同时也可以同时配置两个,可以再配置一个基于client认证的。
        clients.inMemory()
                .withClient("password")
                .authorizedGrantTypes("password", "refresh_token") //授权模式为password和refresh_token两种
                .accessTokenValiditySeconds(1800) // 配置access_token的过期时间
                .resourceIds("rid") //配置资源id
                .scopes("all") // 允许授权范围
                .secret("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") //123加密后的密码
                ;
    }

    /**
     * AuthorizationServerEndpointsConfigurer 访问端点配置 是一个装载类
     * 装载Endpoints所有相关的类配置(AuthorizationServer、TokenServices、TokenStore、ClientDetailsService、UserDetailsService)。
     * tokenService用于存到redis中
     *
     * @param endpoints
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.tokenStore(redisToken) //配置令牌的存到redis
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }

    /**
     * AuthorizationServerSecurityConfigurer继承SecurityConfigurerAdapter.
     * 也就是一个 Spring Security安全配置提供给AuthorizationServer去配置AuthorizationServer的端点(/oauth/****)的安全访问规则、过滤器Filter。
     *
     * @param security
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        // 表示支持 client_id 和 client_secret 做登录认证
        // 允许使用表单认证
        security.allowFormAuthenticationForClients();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kay三石 [Alay Kay]

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值