美文网首页Oauth2
SpringBoot--实战开发--OAuth2.0授权码模式(

SpringBoot--实战开发--OAuth2.0授权码模式(

作者: 无剑_君 | 来源:发表于2019-08-24 23:47 被阅读0次

一、 授权码模式(Authorization Code)简介

授权码模式是四种模式中最繁琐也是最安全的一种模式。

  1. client向资源服务器请求资源,被重定向到授权服务器(AuthorizationServer)
  2. 浏览器向资源拥有者索要授权,之后将用户授权发送给授权服务器
  3. 授权服务器将授权码(AuthorizationCode)转经浏览器发送给client
  4. client拿着授权码向授权服务器索要访问令牌
  5. 授权服务器返回Access Token和Refresh Token给cilent。
    这种模式是四种模式中最安全的一种模式。

一般用于client是Web服务器端应用或第三方的原生App调用资源服务的时候。因为在这种模式中AccessToken不会经过浏览器或移动端的App,而是直接从服务端去交换,这样就最大限度的减小了AccessToken泄漏的风险。

(A)用户访问客户端,后者将前者导向认证服务器。
客户端申请认证的URI,包含以下参数:
response_type:表示授权类型,必选项,此处的值固定为"code"
client_id:表示客户端的ID,必选项
redirect_uri:表示重定向URI,可选项
scope:表示申请的权限范围,可选项
state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。
请求URL:

GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com

(B)用户选择是否给予客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。
服务器回应客户端的URI,包含以下参数:

code:表示授权码,必选项。该码的有效期应该很短,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系。
state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。

HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz

(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
客户端向认证服务器申请令牌的HTTP请求,包含以下参数:
grant_type:表示使用的授权模式,必选项,此处的值固定为"authorization_code"。
code:表示上一步获得的授权码,必选项。
redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。
client_id:表示客户端ID,必选项。

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb

(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
认证服务器发送的HTTP回复,包含以下参数:
access_token:表示访问令牌,必选项。

token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。
expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。
scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。

 HTTP/1.1 200 OK
     Content-Type: application/json;charset=UTF-8
     Cache-Control: no-store
     Pragma: no-cache
     {
       "access_token":"2YotnFZFEjr1zCsicMWpAA",
       "token_type":"example",
       "expires_in":3600,
       "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
       "example_parameter":"example_value"
     }

二、Maven依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

三、代码实现

  1. 实体类
    登录用户名密码,
@Data
@AllArgsConstructor
public class User {
    private long userId;
    private String userName;
    private String password;
}

将SpringUser类与自定义类进行转换:

import org.springframework.security.core.userdetails.User;
/**
 * 自定义UserDetails类 携带User实例
 */
@Data
public class MyUserDetails extends User {
    private com.xtsz.springbootauth2.entity.User user;
    public MyUserDetails(com.xtsz.springbootauth2.entity.User user) {
        super(user.getUserName(), user.getPassword(), true, true, true, true, Collections.EMPTY_SET);
        this.user = user;
    }
}
  1. 用户接口
    继承Spring UserDetailsService 接口:
import org.springframework.security.core.userdetails.UserDetailsService;
public interface UserService extends UserDetailsService {
}

实现类:

@Primary
@Service("userService")
public class UserServiceImpl implements UserService {
    private final static Set<User> users = new HashSet<>();

    static {
        users.add(new User(1, "admin", new BCryptPasswordEncoder().encode("123456")));
    }

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        Optional<User> user = users.stream()
                .filter((u) -> u.getUserName().equals(s))
                .findFirst();
        if (!user.isPresent())
            throw new UsernameNotFoundException("用户不能发现!");
        else
            return UserDetailConverter.convert(user.get());
    }

    private static class UserDetailConverter {
        static UserDetails convert(User user) {
            return new MyUserDetails(user);
        }
    }
}
  1. Security配置
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    /**
     * Spring Boot 2 配置,这里要bean 注入
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        AuthenticationManager manager = super.authenticationManagerBean();
        return manager;
    }

    /**
     * 加密方式
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
  1. 认证服务配置
/**
 * 认证服务配置
 */
@Configuration
@EnableAuthorizationServer
@Slf4j
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userService;

    @Autowired
    private TokenStore tokenStore;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client") // 客户端ID
                .scopes("app") // 允许的授权范围
                .autoApprove(true)  // 如果为true 则不会跳转到授权页面,而是直接同意授权返回code
                .authorizedGrantTypes("authorization_code", "refresh_token") // 设置验证方式
                .redirectUris("http://localhost:8080/callback","http://localhost:8080/signin") //回调地址,也可以配置文件中定义
                .secret(new BCryptPasswordEncoder().encode("123456"))   //必须加密
                .accessTokenValiditySeconds(10000) //token过期时间
                .refreshTokenValiditySeconds(10000); //refresh过期时间;
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore)
                .authenticationManager(authenticationManager)
                .userDetailsService(userService); //配置userService 这样每次认证的时候会去检验用户是否锁定,有效等
    }

    @Bean
    public TokenStore tokenStore() {
        // 使用内存的tokenStore
        return new InMemoryTokenStore();
    }
}

注意:一定要配置回调地址。

  1. Security配置
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    /**
     * Spring Boot 2 配置,这里要bean 注入
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        AuthenticationManager manager = super.authenticationManagerBean();
        return manager;
    }

    /**
     * 加密方式
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

注意:请不要配置资源服务配置

四、测试

  1. 获取令牌


    获取令牌
    获取令牌
    获取令牌
  2. 接口调用


    接口调用

相关文章

网友评论

    本文标题:SpringBoot--实战开发--OAuth2.0授权码模式(

    本文链接:https://www.haomeiwen.com/subject/dtpqectx.html