SpringCloud、SpringBoot2.0 整合Oauth2 (一) 基本配置

以下文章可以辅助理解Oauth2相关概念

Spring Security与OAuth2介绍:https://www.jianshu.com/p/63115c71a590

Spring Security GrantedAuthority(已授予的权限): https://www.cnblogs.com/longfurcat/p/9417422.html

1、添加maven依赖

<!-- web项目启动模块-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security -->
<dependency>  
 <groupId>org.springframework.boot</groupId>  
 <artifactId>spring-boot-starter-security</artifactId>  
</dependency>
<!-- Spring Security Oauth2 -->  
<dependency>  
 <groupId>org.springframework.security.oauth</groupId>  
 <artifactId>spring-security-oauth2</artifactId>  
</dependency> 
<!--Redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、配置 WebSecurity

/**
 * ===================================
 * 描 述 : 授权配置
 * 包 名 : top.qinxq.single.common.auth
 * 创建人 : qinxq
 * ===================================
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig  extends WebSecurityConfigurerAdapter {

    /**
     * =====================================
     * 描   述 : 这一步的配置是必不可少的,否则没有password grant_type
     * 参   数 :  []
     * 返 回 值 : org.springframework.security.authentication.AuthenticationManager
     * 创 建 人 :  qinxq
     * =====================================
     */
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}

3、实现 UserDetailsService 接口

重写 loadUserByUsername 方法。该方法应该返回用户的基本信息,包括权限权限信息,即 UserDetails 对象

/**
 * ===================================
 * 描 述 : 认证数据访问
 * 包 名 : top.qinxq.single.service.impl
 * 创建人 : qinxq
 * ===================================
 */
@Service("userDetailService")
public class UserDetailServiceImpl implements UserDetailsService {
    @Autowired
    private ISysUserService userService;

    @Override
    public UserDetails loadUserByUsername(String username){
        UserVO userVo = userService.findUserByUsername(username);
        if(null == userVo || userVo.getUserId()==null){
            throw new UsernameNotFoundException("用户不存在");
        }
        return new UserDetailsImpl(userVo);
    }
}

以下请按自己的方式实现,以下仅供参考

本例采用 UserVO 和 RoleVO 作为用户和角色的视图传输对象,并单独实现UserDetails。

UserVO

/**
 * ===================================
 * 描 述 : 用户角色信息
 * 包 名 : top.qinxq.single.entity.vo
 * 创建人 : qinxq
 * ===================================
 */
@Data
public class UserVO implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 主键ID
     */
    private Integer userId;
    /**
     * 用户名
     */
    private String username;

    /**
     * 用户昵称/真实姓名
     */
    private String nickName;
    /**
     * 密码
     */
    private String password;
    /**
     * 随机盐
     */
    private String salt;
    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 修改时间
     */
    private Date updateTime;
    /**
     * 0-正常,1-删除,2-锁定
     */
    private Integer status;

    /**
     * 手机
     */
    private String phone;

    /**
     * 角色列表
     */
    private List<RoleVO> roleList;
}

RoleVO

/**
 * ===================================
 * 描 述 : 角色
 * 包 名 : top.qinxq.single.entity.vo
 * 创建人 : qinxq
 * ===================================
 */
@Data
public class RoleVO implements Serializable {
    private static final long serialVersionUID = 1L;

    private Integer roleId;
    private String roleName;
    private String roleCode;
    private String roleDesc;
    private Date createTime;
    private Date updateTime;
    private String isDel;
}

UserDetailsImpl

/**
 * ===================================
 * 描 述 : 重写Security的用户认证信息工具类
 * 包 名 : top.qinxq.single.entity.vo
 * 创建人 : qinxq
 * ===================================
 */
@Data
public class UserDetailsImpl implements UserDetails {
    private static final long serialVersionUID = 1L;
    private Integer userId;
    private String username;
    private String password;
    private Integer status;
    private List<RoleVO> roleList;


    public UserDetailsImpl(UserVO userVo) {
        this.userId = userVo.getUserId();
        this.username = userVo.getUsername();
        this.password = userVo.getPassword();
        this.status = userVo.getStatus();
        roleList = userVo.getRoleList();
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> authorityList = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(roleList)){
            for (RoleVO role : roleList) {
                authorityList.add(new SimpleGrantedAuthority(role.getRoleCode()));
            }
        }
        authorityList.add(new SimpleGrantedAuthority(SecurityConstants.BASE_ROLE));//常量 "ROLE_USER"
        return authorityList;
    }

    @Override
    public boolean isAccountNonExpired() { //账号过期
//        if(null != this.accountExpired && this.accountExpired.compareTo(new Date()) < 0) {
//            return false;
//        }
        return true;
    }

    @Override
    public boolean isAccountNonLocked() { //账号锁定
        return CommonConstant.SYS_USER_STATUS.STATUS_LOCK.ordinal() == status ? false : true;
    }

    @Override
    public boolean isCredentialsNonExpired() { //密码等是否过期
//        if(null != this.passwordExpired && this.passwordExpired.compareTo(new Date()) < 0) {
//            return false;
//        }
        return true;
    }

    @Override
    public boolean isEnabled() {
        return CommonConstant.SYS_USER_STATUS.STATUS_DEL.ordinal() == status ? false : true;
    }


}

4、配置资源服务器

/**
 * ===================================
 * 描 述 : 资源服务器
 * 包 名 : top.qinxq.single.common.auth
 * 创建人 : qinxq
 * ===================================
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {

//        //表单登录 方式
//        http.formLogin()
//                .loginPage("/authentication/require")
//                //登录需要经过的url请求
//                .loginProcessingUrl("/authentication/form");

        http
                .authorizeRequests()
                .antMatchers("/oauth/token").permitAll()
                .anyRequest()
                .authenticated()
                .and()
                //关闭跨站请求防护
                .csrf().disable();
    }
}

5、配置认证服务器

/**
 * ===================================
 * 描 述 : 认证服务器
 * 包 名 : top.qinxq.single.common.auth
 * 创建人 : qinxq
 * ===================================
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    RedisConnectionFactory redisConnectionFactory;

    @Autowired
    private UserDetailsService userDetailService;

    /**
     * =====================================
     * 描   述 : 配置客户端信息
     * 参   数 :  [clients]
     * 返 回 值 : void
     * 创 建 人 :  qinxq
     * =====================================
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //这里直接把配置信息保存在内存中
        clients.inMemory()
                .withClient("app")
                //这里必须使用加密
                .secret(PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("app"))
                // 配置 GrantTypes 支持 刷新token 使用密码模式
                .authorizedGrantTypes("client_credentials","refresh_token","password")
                //服务域
                .scopes("server");
    }

    /**
     * =====================================
     * 描   述 : 配置 Token 的节点 和 Token 服务
     * 参   数 :  [endpoints]
     * 返 回 值 : void
     * 创 建 人 :  qinxq
     * =====================================
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        endpoints.tokenStore(tokenStore())
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailService);
    }

    /**
     * =====================================
     * 描   述 : token 使用内存形式
     * 参   数 :  []
     * 返 回 值 : org.springframework.security.oauth2.provider.token.TokenStore
     * 创 建 人 :  qinxq
     * =====================================
     */
    @Bean
    public TokenStore tokenStore(){
        return new InMemoryTokenStore();
    }
    
    /**
     * =====================================
     * 描   述 : 密码加密方式
     * 参   数 :  []
     * 返 回 值 : org.springframework.security.crypto.password.PasswordEncoder
     * 创 建 人 :  qinxq
     * =====================================
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }


}

6、测试

获取token(/oauth/token)
  • 请求地址: localhost:port/oauth/token

  • 请求方法: POST

  • 请求头:

    请求头中,需要传入一个Authorization 参数

    参数值为 Oauth2 配置客户端中的Client与secret以 'Client:secret’的形式组合,然后再进行 Base64 编码

    本例为Base64(app:app);

  • 请求参数:

    • grant_type : password (授权模式)
    • username :[用户名]
    • password :[密码]
      在这里插入图片描述
访问受保护的资源
  • 请求头: Authorization 值为 bearer + 空格 + token

    ps:bearer 为 获取token 时,返回的 token_type 值
    在这里插入图片描述

相关链接

SpringCloud、SpringBoot2.0 整合Oauth2 (一) 基本配置

SpringCloud、SpringBoot2.0 整合Oauth2 (一) 基本配置

SpringCloud、SpringBoot2.0 整合Oauth2 (二) 自定义返回格式及用户基本信息

SpringCloud、SpringBoot2.0 整合Oauth2 (二) 自定义返回格式及用户基本信息

发布了48 篇原创文章 · 获赞 34 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/u014481096/article/details/102746125