SpringCloud oauth整合JWT(一) -- 思路介绍与认证服务器实现

注意:这里只是讲解oauth与jwt整合,不过多的介绍单个技术

1.oauth 2.0

OAuth2.0是OAuth协议的延续版本,但不向前兼容OAuth 1.0(即完全废止了OAuth1.0)。 OAuth 2.0关注客户端开发者的简易性。要么通过组织在资源拥有者和HTTP服务商之间的被批准的交互动作代表用户,要么允许第三方应用代表用户获得访问的权限。同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。

2.JWT

JSON Web Token(JWT)是一个非常轻巧的规范,这个规范允许我们使用JWT在用户与服务器传递安全可靠的信息。

3.为什么需要两者结合

首先,这些的前提都是在分布式服务的前提下,如果你的服务或者是应用不考虑分布式,甚至是不考虑用户的使用感受,那么你可以有无数种实现权限控制的方式,但如果如博主所说需要oauth与jwt,那么就说明你的服务是复杂的,是需要更加严谨的权限控制,那么请读者仔细的阅读博文的以下内容。

  • 单独使用jwt:进行加密后会生成一串字符串,可以用加密的秘钥进行解密,可以将用户信息进行加密当做token进行登录的验证(只能存储非隐私的信息),但是无法完成对某个特定权限的控制和对某些接口的权限限制,短板较多且不够灵活。
  • 单独使用oauth:在单个项目组成的项目中,oauth可以完成认证与授权,但在多服务的情况下,做单点登录的时候需要不停的访问认证服务器,这时候或造成认证服务器的高压,且会影响接口的响应效率
  • 两者结合:jwt将用户的信息加密后生成有意义的token,而不是oauth那样生成的没有任何意义的UUID,然后oauth又有很优秀的权限控制体系,直接用jwt的token就可以下多个服务间进行使用,结合jwt的token****于oauth的权限控制,可以加强服务安全体系的完善。

3.认证服务器

1.pom依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-oauth2artifactId>
        dependency>

        <dependency>
            <groupId>io.jsonwebtokengroupId>
            <artifactId>jjwtartifactId>
            <version>0.9.0version>
        dependency>

        <dependency>
            <groupId>org.springframework.securitygroupId>
            <artifactId>spring-security-jwtartifactId>
            <version>1.0.9.RELEASEversion>
        dependency>

        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.16.20version>
        dependency>

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.47version>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redis-reactiveartifactId>
        dependency>
    dependencies>

2.security配置

package com.wangle.server.config;

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.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;


@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // 注入用户自定义认证逻辑
    @Autowired
    private MyUserDetailService userDetailService;

    @Autowired
    private PasswordEncoder passwordEncoder;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers()
                .antMatchers("/**")
                .and()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin().permitAll();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService)
                .passwordEncoder(passwordEncoder); //密码加密
    }
}

2.tokenStore配置

package com.wangle.server.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

@Configuration
public class TokenStoreConfig {

    // 注入redis连接工厂
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

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

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

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        accessTokenConverter.setSigningKey("test_key");//配置JWT使用的秘钥
        return accessTokenConverter;
    }

    @Bean
    public JwtTokenEnhancer jwtTokenEnhancer() {
        return new JwtTokenEnhancer();
    }

}

3.认证配置

package com.wangle.server.config;

import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
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.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import java.nio.charset.StandardCharsets;
import java.util.*;

@Configuration
@EnableAuthorizationServer
public class ServerConfig extends AuthorizationServerConfigurerAdapter {

    // 注入认证管理器
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private TokenStore redisTokenStore;

    // 注入用户自定义认证逻辑
    @Autowired
    private MyUserDetailService userDetailService;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private TokenStore jwtTokenStore;

    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;

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

    //定义客户端的信息
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient("wangle")
                .secret(passwordEncoder.encode("wangle"))
                // .resourceIds("admin","auth")
                //设置token的有效期,不设置默认12小时
                //.accessTokenValiditySeconds(client.getAccessTokenValidatySeconds())
                //设置刷新token的有效期,不设置默认30天
                //.refreshTokenValiditySeconds(client.getRefreshTokenValiditySeconds())
                .redirectUris("http://www.baidu.com")
                .authorizedGrantTypes("authorization_code","client_credentials", "refresh_token", "password")
                .scopes("all", "read", "write")
                .accessTokenValiditySeconds(10)
                .autoApprove(true);
    }


    //自定义web响应错误翻译器
    /*
     * @Bean public WebResponseExceptionTranslator webResponseExceptionTranslator(){
     * return new MssWebResponseExceptionTranslator(); }
     */

    /**
     * 

* 注意,自定义TokenServices的时候,需要设置@Primary,否则报错, *

* * @return */
@Primary @Bean public DefaultTokenServices myTokenServices() { DefaultTokenServices tokenServices = new DefaultTokenServices(); tokenServices.setTokenStore(jwtTokenStore); tokenServices.setSupportRefreshToken(true); // tokenServices.setClientDetailsService(clientDetails()); // token有效期自定义设置,默认12小时 tokenServices.setAccessTokenValiditySeconds(60 * 60 * 12); // refresh_token默认30天 tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7); return tokenServices; } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { TokenEnhancerChain enhancerChain = new TokenEnhancerChain(); List<TokenEnhancer> delegates = new ArrayList<>(); delegates.add(jwtTokenEnhancer); //配置JWT的内容增强器 delegates.add(jwtAccessTokenConverter); enhancerChain.setTokenEnhancers(delegates); endpoints .userDetailsService(userDetailService) //定义权限终点的token存储方式 .tokenStore(redisTokenStore) //定义权限终点的user认证逻辑 .accessTokenConverter(jwtAccessTokenConverter) //定义权限终点的认证管理器 .authenticationManager(authenticationManager) //jwt增强 //.tokenEnhancer(enhancerChain) //认证异常翻译 //endpoints.exceptionTranslator(webResponseExceptionTranslator()); ; } }

ClientDetailsServiceConfigurer可以使用以上的,也可以配置成从数据库获取,具体的实现方式这里不做介绍。

3.自定义用户验证实现

package com.wangle.server.config;

import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.HashSet;

@Service("userDetailService")
public class MyUserDetailService implements UserDetailsService {

	@Autowired
	private PasswordEncoder passwordEncoder;

	// 重写loadUserByUsername(实现用户认证逻辑)
	@Override
	public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {

		// 初始化权限集合
		HashSet<GrantedAuthority> authorities = new HashSet<>();
		// 可用性 :true:可用 false:不可用
		boolean enabled = true;
		// 过期性 :true:没过期 false:过期
		boolean accountNonExpired = true;
		// 有效性 :true:凭证有效 false:凭证无效
		boolean credentialsNonExpired = true;
		// 锁定性 :true:未锁定 false:已锁定
		boolean accountNonLocked = true;

		// 角色名必须是以ROLE_开头,不然权限注解无法识别此角色
		SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_ADMIN");
		authorities.add(grantedAuthority);

		User principle = new User(mobile,passwordEncoder.encode("123456"), enabled, accountNonExpired,
				credentialsNonExpired, accountNonLocked, authorities);
		return principle;
	}
}

4.请求地址

请求获取token的地址就可以获取token
SpringCloud oauth整合JWT(一) -- 思路介绍与认证服务器实现_第1张图片

你可能感兴趣的:(springcloud)