spring security oauth2授权码模式

spring security oauth2授权码模式

这里我使用的spring security版本为5.x,spring boot版本为2.x。
授权码模式是Oauth2协议最严格、流程最完整的授权模式,流程如下:
在这里插入图片描述

  • A.客户端将用户导向认证服务器
  • B.用户决定是否给客户端授权
  • C.用户同意给客户端授权,认证服务器将用户导向客户端提供的url地址,并返回授权码
  • D客户端通过重定向url和授权码想授权服务器申请令牌
  • E.认证服务器验证通过后返回令牌给客户端

在A步骤中,向认证服务器请求的url中,应包含以下参数:

  • response_type:表示授权类型,必选项,此处的值固定为code,标识授权码模式
  • client_id:表示客户端的ID,必选项
  • redirect_uri:表示重定向URI,可选项
  • scope:表示申请的权限范围,可选项
  • state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。

认证服务器返回授权码后,在D步骤中想认证服务器申请令牌请求中,应包含以下参数:

  • grant_type:表示使用的授权模式,必选项,此处的值固定为authorization_code
  • code:表示上一步获得的授权码,必选项。
  • redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。
  • client_id:表示客户端ID,必选项。

介绍完认证流程,直接上代码
首先编写认证服务器代码,创建一个spring boot工程,我这里项目名叫做security-oauth2-server,端口为8085,添加依赖

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


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter</artifactId>
        </dependency>
        <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.apache.commons</groupId>
            <artifactId>commons-lang3</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>

在启动类上添加@EnableAuthorizationServer注解开启认证服务。这里我模拟了从数据库获取用户信息,所以要实现UserDetailsService,它只有一个抽象方法loadUserByUsername,它返回的是一个UserDetails对象,它是一个接口,所以我自定义了一个SysUser对象实现了它,代码如下

public class SysUser implements UserDetails {

    private String username;

    private String password;
	
    private boolean accountNonExpired = true;//账户是否为过期

    private boolean accountNonLocked = true;//账号是否未锁定

    private boolean credentialsNonExpired = true;//凭证是否未过期,也是就是密码

    private boolean enabled = true;//账户是否可用

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        GrantedAuthority authority = new SimpleGrantedAuthority("USER");
        ArrayList<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        grantedAuthorities.add(authority);
        return grantedAuthorities;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public boolean isAccountNonExpired() {
        return accountNonExpired;
    }

    public void setAccountNonExpired(boolean accountNonExpired) {
        this.accountNonExpired = accountNonExpired;
    }

    public boolean isAccountNonLocked() {
        return accountNonLocked;
    }

    public void setAccountNonLocked(boolean accountNonLocked) {
        this.accountNonLocked = accountNonLocked;
    }

    public boolean isCredentialsNonExpired() {
        return credentialsNonExpired;
    }

    public void setCredentialsNonExpired(boolean credentialsNonExpired) {
        this.credentialsNonExpired = credentialsNonExpired;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }
}

因为在返回的对象中,它的属性中所有boolean类型的值都要为true,并且权限集合不能为空,所以在这里我给它进行了初始化。然后是编写UserDetailsService的实现类UserService类,代码如下:

@Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser user = new SysUser();
        user.setUsername(username);
        user.setPassword(passwordEncoder.encode("root"));
        return user;
    }

登录的用户名可以是任意的,但是密码一定是root在这个版本里,用户密码默认是要加密,加密的工具类可以在启动类中注入,代码如下:

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

编写spring security配置类

@Configuration //开启权限验证
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }
}

编写认证服务配置类

@Configuration
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Bean
    public TokenStore tokenStore(){
        return new InMemoryTokenStore();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("xj-client")//客户端id
                .authorizedGrantTypes("authorization_code","refresh_token")//授权方式
                .scopes("all")//授权权限
                .secret(passwordEncoder.encode("xj-client"));//客户端密码,要加密
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.checkTokenAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()").allowFormAuthenticationForClients();
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager)
                .tokenStore(tokenStore())
                .userDetailsService(userDetailsService);
    }
}

认证服务器的代码到此就编写好了,然后就是编写资源服务器的代码。
创建一个spring boot工程,我这项目名叫security-resource-server,访问端口为8084添加依赖

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter</artifactId>
        </dependency>
        <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.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </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>

在配置类上添加@EnableResourceServer开启资源服务,编写application.yml配置文件

auth-server-url: http://localhost:8085
server:
  port: 8084
security:
  oauth2:
    client:
      client-secret: xj-client
      client-id: xj-client
      scope: all
      access-token-uri: ${auth-server-url}/oauth/token #申请令牌地址
      user-authorization-uri: ${auth-server-url}/oauth/authorize #用户授权地址
    resource:
      token-info-uri: ${auth-server-url}/oauth/check_token #检查令牌

编写一个TestController用于测试

@RestController
public class TestController {

    private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);

    @GetMapping("index")
    public Object index(Authentication authentication){
        LOGGER.info("resource: user {}",authentication);
        return authentication;
    }
}

到此,代码就编写好了,可以测试一下。把两个项目都启动,先向授权服务器申请授权码,在浏览器地址栏输入http://localhost:8085/oauth/authorize?response_type=code&client_id=xj-client&redirect_uri=http://baidu.com
在这里插入图片描述
输入用户名密码登录,进入到授权页面
在这里插入图片描述
选择Approve,点击Authorize同意授权,页面会从定向到你提供的uri地址,并在后面附上授权码
在这里插入图片描述
然后就可以用这个授权码去授权令牌,打开postman,输入一下内容
在这里插入图片描述在这里插入图片描述
使用Basic Auth登录相当于在请求头中添加一个keyAuthorization值为client_id:client_secret结果base64加密后的字符串,下面返回的access_token的值就是令牌,,现在可以使用这个令牌去资源服务器访问了
在这里插入图片描述
查看:完整代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值