Spring Security OAuth2 JWT 基本上应用

Spring Security OAuth2 JWT 基本上应用

前边学了 Spring Security 新手入门,如今配搭 oauth2 JWT 开展检测。

1、什么叫 OAuth2

OAuth 是一个有关受权(authorization)的对外开放互联网规范,促使第三方应用能够应用该动态口令在限制時间限制范畴浏览特定資源。在全球获得广泛运用,现阶段的版本号是2.0版。

1.1、有关 OAuth2 的好多个关键定义:

  • resource owner: 有着被浏览資源的客户
  • user-agent: 一般来说便是电脑浏览器
  • client: 第三方应用
  • Authorization server: 验证网络服务器,用于开展用户认证并授予token
  • Resource server:資源网络服务器,有着被浏览資源的网络服务器,必须根据token来明确是不是有管理权限浏览

1.2、挥手步骤

确立定义后,能看 OAuth2 的协议书挥手步骤,节选自RFC6749

image-20210519120117924

(A)客户开启手机客户端之后,手机客户端规定客户给与受权。

(B)客户愿意给与手机客户端受权。

(C)手机客户端应用上一步得到的受权,向验证网络服务器申请办理动态口令。

(D)验证网络服务器对手机客户端开展验证之后,确定准确无误,愿意派发动态口令。

(E)手机客户端应用动态口令,向資源网络服务器申请办理获得資源。

(F)資源网络服务器确定动态口令准确无误,愿意向手机客户端对外开放資源

1.3、受权方式

oauth2依据应用情景不一样,分为了4种方式

  • 授权码方式(authorization code)
  • 简单化方式(implicit)
  • 登陆密码方式(resource owner password credentials)
  • 手机客户端方式(client credentials)

授权码方式应用到回调函数详细地址,是更为繁杂的方法,一般网址中经常会出现的新浪微博,qq第三方登录,都是会选用这一方式。简单化方式不常见。

2、配备

应用oauth2保护你的运用,能够分成简单的分成三个流程

  • 配备資源网络服务器
  • 配备受权网络服务器
  • 配备spring security

2.1、maven 依靠配备

这儿立即引进 spring-cloud oauth2,更为便捷以后的扩展。

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

<dependencies>
    <!--spring cloud oauth2-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
    <!--spring cloud security-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-security</artifactId>
    </dependency>
    <!--JWT-->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
</dependencies>

<!--spring cloud-->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.SR2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

2.2、配备受权网络服务器

这儿必须开展浏览手机客户端的配备,并配备受权种类和access_tokenjwtToken

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailServiceImpl userDetailService;
    @Autowired
    @Qualifier("jwtTokenStore")
    private TokenStore tokenStore;
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;

    /**
     * 配备受权种类
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //设定Jwt內容提高
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> list = new ArrayList<>();
        list.add(jwtTokenEnhancer);
        list.add(jwtAccessTokenConverter);
        tokenEnhancerChain.setTokenEnhancers(list);

        endpoints
                //登陆密码方式务必配备
                .authenticationManager(authenticationManager)
                //登陆密码方式务必配备
                .userDetailsService(userDetailService)
                //accessToken转JwtToken
                .tokenStore(tokenStore)
                .accessTokenConverter(jwtAccessTokenConverter)
                //jwt內容提高
                .tokenEnhancer(tokenEnhancerChain);
    }

    /**
     * 配备手机客户端详细信息信息内容
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.
                //根据运行内存配备
                inMemory()
                //手机客户端ID
                .withClient("client")
                //密匙
                .secret(bCryptPasswordEncoder.encode("112233"))
                //跳转详细地址
                .redirectUris("http://www.baidu.com")
                //受权范畴
                .scopes("all")
                //accessToken有效时间
                .accessTokenValiditySeconds(60)
                //refreshToken有效时间
                .refreshTokenValiditySeconds(3600)
                /**
                 * 受权种类
                 * authorization_code:授权码方式
                 * password:登陆密码方式
                 * refresh_token:更新动态口令
                 */
                .authorizedGrantTypes("authorization_code", "password", "refresh_token");
    }
}

2.3、配备資源网络服务器

承继 ResourceServerConfigurerAdapter并加上 @EnableResourceServer注释

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //阻拦全部要求
                .anyRequest()
                .authenticated()
                .and()
                //spring secuity给予了requestMatchers插口,等额的于http.authorizeRequests().anyRequest().access("permitAll");
                //给予資源,浏览/user必须管理权限验证
                .requestMatchers()
                .antMatchers("/user/**");
    }
}

2.4、JWT 配备

2.4.1、accessToken 转 JwtToken 配备类

关键工作中是建立 JwtAccessTokenConverter并设定密匙,并引入到 Bean 管理方法器皿中。

/**
 * accessToken转JwtToken配备
 */
@Configuration
public class JwtTokenStoreConfig {

    @Bean
    public JwtTokenStore jwtTokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        //设定jwt密匙
        jwtAccessTokenConverter.setSigningKey("test_key");
        return jwtAccessTokenConverter;
    }

    @Bean
    public JwtTokenEnhancer jwtTokenEnhancer() {
        return new JwtTokenEnhancer();
    }
}
2.4.2、JwtToken內容扩展配备类

当 accessToken 转 jwtToken时,假如想往动态口令中添加自定客户信息,比如登陆时间点,能够配备下列类:

/**
 * JwtToken內容扩展配备类
 * @author Lin
 */
public class JwtTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
        Map<String, Object> map = new HashMap<>();
        map.put("enhance", "enhance info");       ((DefaultOAuth2AccessToken)oAuth2AccessToken).setAdditionalInformation(map);
        return oAuth2AccessToken;
    }
}

2.5、配备 spring security

/**
 * spring security配备类
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 密码加密
     *
     * @return
     */
    @Bean
    public BCryptPasswordEncoder getPasswordEncode() {
        return new BCryptPasswordEncoder();
    }

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

    @Override
    @Bean
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }
}

2.6、完成 UserDetailsService

完成 UserDetailService 用以登陆认证,及其登陆密码方式下必须采用。

@Service
public class UserDetailServiceImpl implements UserDetailsService {

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        String password = bCryptPasswordEncoder.encode("123456");
        return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("permission1"));
    }
}

建立 Userdao层以下(非务必):

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;
    }
}

3、运作检测

3.1、获得授权码

立即浏览 /oauth/authorize? 插口能够得到授权码

在我的新项目中浏览途径以下:

http://localhost:8080/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=all

电脑浏览器浏览,自动跳转到http://localhost:8080/login.html默认设置登录页,点一下登陆,受权:

image-20210519171414311

自动跳转到https://www.baidu.com/?code=XKee3V网页页面,XKee3v便是得到的授权码。

3.2、依据授权码方式得到动态口令

运用 postman 检测,浏览

http://localhost:8080/oauth/token

配备 Authorization 信息内容,即登陆手机客户端的账户和登陆密码;

image-20210519175250709

配备 Body 信息内容,grant_type的变量值是 authorization_code,authorization_code即是授权码方式,code即是上文得到的授权码。

image-20210519175644261

配备完后运作检测,回到 access_tokenrefresh_token,见到 access_token取得成功变为JwtToken

image-20210519175909997

3.3、登陆密码方式

登陆密码方式比授权码方式简单一点,不用得到授权码,立即忽视上文获得授权码的实际操作,只需略微修改配备信息内容。

Authorization 信息内容不用修改,改动 Body 信息内容, grant_type的变量值改成 password,意味着登陆密码方式,填好登陆 spring security 的账户和登陆密码。

image-20210519180556700

3.4、更新动态口令

在上原文中我设定了 access_token的及时性为60秒,当access_token无效时,必须依据refresh_token获得新的动态口令。

浏览途径以下:

http://localhost:8080/oauth/token

Authorization 配备信息内容以下:

image-20210519181055985

Body 必须配备 grant_type的变量值为 refresh_token,意味着更新动态口令,并填好refresh_token的变量值。浏览后就可以得到新的 access_token

image-20210519181404842

3.5、依据 access_token得到資源

浏览途径以下:

http://localhost:8080/user/getCurrentUser

Header 请求头加上 Authorization 主要参数,并设定变量值为 bearer 空格符 access_token,就可以得到插口传参。

image-20210519181904047

4、参考文献

bilbil Spring Security实例教程

阮一峰-OAuth2.0

评论(0条)

刀客源码 游客评论