博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring boot --- Spring Oauth(一)
阅读量:6487 次
发布时间:2019-06-24

本文共 9177 字,大约阅读时间需要 30 分钟。

   文章部分图片来自参考资料,这篇文章主要讲 spring security  oauth

概述

         上一篇我们学习了 SS 中重要的工作原理和几个大概的认证和授权过程。而 spring security oauth 用到的就是 spring security 知识,我们学习 sso 之前先看一下oauth 是什么,可以学习

         oauth 的流程图如下 : (牢牢记住这张图)

       主要的角色有资源持有者,资源服务器,认证服务器,还有用户

       授权(获取 Access Token)的方式有多种方式

  • 授权码
  • 简化模式
  • 客户端模式
  • 密码模式

     oauth 可以理解成工作中,你(Client)去出差,回来需要报销,会计(Authorzation Server)首先需要你请示老板(Resource Owned)是否同意给你报销出差费用,假如同意了,你就回来找会计,把老板的凭证给她,她会给你一个token (获取token过程的方式有多种,就是前面提到的), 然后你带着 token 再去财务(Resource Server)领钱 ,结束流程。

 

Spring Security Oauth

    学习 Spring Security Oauth  ,先学习一个例子(),然后根据例子配合oauth 流程学习

         我们按照上面的例子敲完代码后,整个流程走完再结合oauth 授权的流程

    例子中使用的授权码,而获取Access Token ,为何先给授权码,而不直接给 Access Token 呢 ?

    给授权码,再用授权码去获取Access Token 的原因是授权码可以让服务端知道client 的身份。

spring security oauth 角色

         oauth 获取中几个重要的角色中在 spring security oauth 中对应的有 :

  • @EnableResourceServer : 作为资源服务器
  • @EnableAuthorazaitonServer : 作为认证中心
  • @EnableOauthClient :做用被认证的客户端,例如提供某个方式去认证 Github 或是 Facebook 的应用

          Resource Owned 的角色放在 Authorazation Server,就是代码中的 UserDetail 。上面三个注解经常会混淆,我们需要记住它们到底实现的用途是什么,还有另外一个注解 : @EnableSSO

UserDetail

         UserDetail 的作用是用来认证即是上面oauth 流程图的A 步骤,示例如下 :

@Servicepublic class MyUserDetailsService implements UserDetailsService {    @Autowired    private UserService userService;    /**     * 授权的时候是对角色授权,而认证的时候应该基于资源,而不是角色,因为资源是不变的,而用户的角色是会变的     */    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        SysUser sysUser = userService.getUserByName(username);        if (null == sysUser) {            throw new UsernameNotFoundException(username);        }        List
authorities = new ArrayList<>(); for (SysRole role : sysUser.getRoleList()) { for (SysPermission permission : role.getPermissionList()) { authorities.add(new SimpleGrantedAuthority(permission.getCode())); } } return new User(sysUser.getUsername(), sysUser.getPassword(), authorities); }}@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter { ... @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder()); }}

 

TokenStore

          获取token,那么很明显需要一个储存token 的容器,例如我们想使用 Redis 来存储token ,当我们需要实现自己token 存储容器时可以如下使用 :

@Configuration@EnableAuthorizationServerpublic class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {    ...     /**     * 该方法是用来配置Authorization Server endpoints的一些非安全特性的,比如token存储、token自定义、授权类型等等的     * 默认情况下,你不需要做任何事情,除非你需要密码授权,那么在这种情况下你需要提供一个AuthenticationManager     */    @Override    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {        endpoints.authenticationManager(authenticationManager)                .tokenStore(new MyRedisTokenStore(redisConnectionFactory));    }} @Servicepublic class MyRedisTokenStore implements TokenStore {   .... }

 

clientDetail

         使用授权码方式获取 Access Token 时先发放授权码,而是否可以发送授权码需要验证client 的身份,clientDetail 便是便是表述 client 基本信息的类

@Configuration@EnableAuthorizationServerpublic class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {        ...    @Resource    private DataSource dataSource;    /**     * 配置ClientDetailsService     * 注意,除非你在下面的configure(AuthorizationServerEndpointsConfigurer)中指定了一个AuthenticationManager,否则密码授权方式不可用。     * 至少配置一个client,否则服务器将不会启动。     */    @Override    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {        clients.jdbc(dataSource);    }    }

 

token 和 clientDetail 基类表述

         可以构建自己的 token 和 clientDetail ,以下是数据库实现

-- used in tests that use HSQLcreate table oauth_client_details (    client_id VARCHAR(256) PRIMARY KEY,    resource_ids VARCHAR(256),    client_secret VARCHAR(256),    scope VARCHAR(256),    authorized_grant_types VARCHAR(256),    web_server_redirect_uri VARCHAR(256),    authorities VARCHAR(256),    access_token_validity INTEGER,    refresh_token_validity INTEGER,    additional_information VARCHAR(4096),    autoapprove VARCHAR(256));create table oauth_client_token (    token_id VARCHAR(256),    token LONGVARBINARY,    authentication_id VARCHAR(256) PRIMARY KEY,    user_name VARCHAR(256),    client_id VARCHAR(256));create table oauth_access_token (    token_id VARCHAR(256),    token LONGVARBINARY,    authentication_id VARCHAR(256) PRIMARY KEY,    user_name VARCHAR(256),    client_id VARCHAR(256),    authentication LONGVARBINARY,    refresh_token VARCHAR(256));create table oauth_refresh_token (    token_id VARCHAR(256),    token LONGVARBINARY,    authentication LONGVARBINARY);create table oauth_code (    code VARCHAR(256), authentication LONGVARBINARY);create table oauth_approvals (    userId VARCHAR(256),    clientId VARCHAR(256),    scope VARCHAR(256),    status VARCHAR(10),    expiresAt TIMESTAMP,    lastModifiedAt TIMESTAMP);-- customized oauth_client_details tablecreate table ClientDetails (    appId VARCHAR(256) PRIMARY KEY,    resourceIds VARCHAR(256),    appSecret VARCHAR(256),    scope VARCHAR(256),    grantTypes VARCHAR(256),    redirectUrl VARCHAR(256),    authorities VARCHAR(256),    access_token_validity INTEGER,    refresh_token_validity INTEGER,    additionalInformation VARCHAR(4096),    autoApproveScopes VARCHAR(256));

 

 

完整服务端配置

@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {    @Autowired    private MyUserDetailsService myUserDetailsService;    @Override    protected void configure(HttpSecurity http) throws Exception {        http.authorizeRequests()                .antMatchers("/oauth/**","/login/**", "/logout").permitAll()                .anyRequest().authenticated()   // 其他地址的访问均需验证权限                .and()                .formLogin()                .loginPage("/login")                .and()                .logout().logoutUrl("/logout").logoutSuccessUrl("/login");    }    @Override    public void configure(WebSecurity web) throws Exception {        web.ignoring().antMatchers("/assets/**");    }    @Override    protected void configure(AuthenticationManagerBuilder auth) throws Exception {        auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());    }    @Bean    @Override    public AuthenticationManager authenticationManager() throws Exception {        return super.authenticationManager();    }    @Bean    public PasswordEncoder passwordEncoder() {        return new BCryptPasswordEncoder();    }}

 

@Configuration@EnableAuthorizationServerpublic class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {    @Resource    private DataSource dataSource;    @Autowired    RedisConnectionFactory redisConnectionFactory;    @Autowired    private AuthenticationManager authenticationManager;    /**     * 配置授权服务器的安全,意味着实际上是/oauth/token端点。     * /oauth/authorize端点也应该是安全的     * 默认的设置覆盖到了绝大多数需求,所以一般情况下你不需要做任何事情。     */    @Override    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {        super.configure(security);    }    /**     * 配置ClientDetailsService     * 注意,除非你在下面的configure(AuthorizationServerEndpointsConfigurer)中指定了一个AuthenticationManager,否则密码授权方式不可用。     * 至少配置一个client,否则服务器将不会启动。     */    @Override    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {        clients.jdbc(dataSource);    }    /**     * 该方法是用来配置Authorization Server endpoints的一些非安全特性的,比如token存储、token自定义、授权类型等等的     * 默认情况下,你不需要做任何事情,除非你需要密码授权,那么在这种情况下你需要提供一个AuthenticationManager     */    @Override    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {        endpoints.authenticationManager(authenticationManager)                .tokenStore(new MyRedisTokenStore(redisConnectionFactory));    }}

        当资源服务器和认证服务器是同一个服务器的时候  :

Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {    @Autowired    private MyUserDetailsService myUserDetailsService;    @Override    protected void configure(HttpSecurity http) throws Exception {        http.authorizeRequests()                .antMatchers("/oauth/**","/login/**", "/logout").permitAll()                .anyRequest().authenticated()   // 其他地址的访问均需验证权限                .and()                .formLogin()                .loginPage("/login")                .and()                .logout().logoutUrl("/logout").logoutSuccessUrl("/login");    }    @Override    public void configure(WebSecurity web) throws Exception {        web.ignoring().antMatchers("/assets/**");    }    @Override    protected void configure(AuthenticationManagerBuilder auth) throws Exception {        auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());    }    @Bean    @Override    public AuthenticationManager authenticationManager() throws Exception {        return super.authenticationManager();    }    @Bean    public PasswordEncoder passwordEncoder() {        return new BCryptPasswordEncoder();    }}

 

工作流程

 

总结

         文章并不是 spring oauth 的入门篇,主要是结合 oauth 的流程图找到对应 spring security oauth 框架中的逻辑对应,更好地自定义改造。结合下面的参考资料,可以完成单点登录的功能。

参考资料

  • (自定义filter,用到的时候推荐一看)
  • 下面是 spring 文档
  • 下面是 spring oauth client 的资料
  • JWT 的介绍
  • 可能遇到的问题

转载于:https://www.cnblogs.com/Benjious/p/10638913.html

你可能感兴趣的文章
将List<int> 转换为用逗号连接为字符串
查看>>
C/C++中extern关键字详解
查看>>
Eclipse 最有用的快捷键
查看>>
K & DN 的前世今生(微软开源命名变革)
查看>>
--@angularJS--angular与BootStrap3的应用
查看>>
Flask服务入门案例
查看>>
ReadWriteLock与ReentrantReadWriteLock
查看>>
Atitit.软件命名空间 包的命名统计 及命名表(2000个名称) 方案java package...
查看>>
新手指导:教你如何查看识别hadoop是32位还是64位
查看>>
Codeforces Round #180 (Div. 2) D. Fish Weight 贪心
查看>>
Gradle sourceCompatibility has no effect to subprojects(转)
查看>>
百度指数分析
查看>>
使用Mkdocs构建你的项目文档
查看>>
三分钟读懂TT猫分布式、微服务和集群之路
查看>>
fn project 运行时配置选项
查看>>
你的leader还在考核你的千行代码Bug率吗?
查看>>
多块盘制作成一个lvm
查看>>
InnoDB多版本
查看>>
关于azkaban上传job压缩包报错问题的解决方案
查看>>
JS版日期格式化和解析工具类,毫秒级
查看>>