springSecurity+Oauth2.0之授权模式

SpringSecurity+Oauth2.0之授权模式

1: 客户端模式:

          客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题。

maven依赖:

 <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-security</artifactId>
 </dependency>
    <!--security-oauth2-->
  <dependency>
      <groupId>org.springframework.security.oauth</groupId>
      <artifactId>spring-security-oauth2</artifactId>
      <version>2.3.3.RELEASE</version>
   </dependency>

其他使用到的依赖:

   <!--操作数据库-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!-- MySQL 驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- Druid 数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.22</version>
        </dependency>

        <!-- region MyBatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
View Code

创建Oauth2.0需要创建三个相关的表,直接使用官方的SQL脚本即可生成(不要修改表名和字段名)

OAuth2 官方的项目中可以找到:https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql

-- ----------------------------
-- Table structure for oauth_access_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token`  (
  `token_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `token` blob NULL,
  `authentication_id` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `user_name` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `client_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `authentication` blob NULL,
  `refresh_token` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`authentication_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
 
-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details`  (
  `client_id` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `resource_ids` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `client_secret` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `scope` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `authorized_grant_types` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `web_server_redirect_uri` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `authorities` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `access_token_validity` int(11) NULL DEFAULT NULL,
  `refresh_token_validity` int(11) NULL DEFAULT NULL,
  `additional_information` varchar(4096) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `autoapprove` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
 
-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token`  (
  `token_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `token` blob NULL,
  `authentication` blob NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
————————————————
View Code

配置yml:

server:
  port: 8082


mybatis:
  mapper-locations: classpath*:mapper/*Mapper.xml
  configuration:
      database-id: MySQL
     # 开启驼峰转换
      map-underscore-to-camel-case: true
      # spring boot集成mybatis的方式打印sql
#      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl


spring:
  application:
    name: my-security-study
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/my-study?useSSL=false&serverTimezone=GMT%2B8&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false
      username: root
      password: 123456
      filters: stat
      # 设置最大数据库连接数,设为0为无限制
      maxActive: 20
      # 配置初始化大小、最小、最大
      initialSize: 1
      #  最大等待时间
      maxWait: 60000
      # 始终保留在池中的最小连接数,如果连接验证失败将缩小至此值
      minIdle: 1
      timeBetweenEvictionRunsMillis: 6000
#      连接在池中保持空闲而不被回收的最小时间(毫秒)
      minEvictableIdleTimeMillis: 30000
      validationQuery: select 'x'
#      对池中空闲的连接是否进行验证,验证失败则回收此连接(默认为false)
      testWhileIdle:  true
#       当从连接池中取出一个连接时是否进行验证,若验证失败则从池中删除该连接并尝试取出另一个连接
      testOnBorrow: true
#      当一个连接使用完归还到连接池时是否进行验证
      testOnReturn: false
#      启用游标缓存,这个对数据库的性能提升很大
      poolPreparedStatements: true
#        要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
      maxOpenPreparedStatements: 20
      filter:
        stat:
          log-slow-sql: true
          slow-sql-millis: 2000
View Code

创建自己的用户表、角色、权限表:

CREATE TABLE `user` (
  `id` int(64) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(32) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `enable` tinyint(4) DEFAULT NULL,
  `locked` tinyint(4) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;


CREATE TABLE `role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) DEFAULT NULL,
  `description` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;


CREATE TABLE `user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `role_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;


CREATE TABLE `resources` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `pattern` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;


CREATE TABLE `role_resource` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_id` int(11) DEFAULT NULL,
  `resource_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

配置认证服务器:

/**
 * @Author dw
 * @ClassName AuthorizationServerConfig
 * @Description 配置认证服务器
 * @Date 2020/4/20 14:47
 * @Version 1.0
 */

@Configuration
@EnableAuthorizationServer // 这个注解告诉 Spring 这个应用是 OAuth2 的授权服务器//
// 提供/oauth/authorize,/oauth/token,/oauth/check_token,/oauth/confirm_access,/oauth/error
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private MyUserServiceImpl myUserService;


    @Bean
    public TokenStore tokenStore() {
//        return new InMemoryTokenStore(); //使用内存中的 token store
        return new JdbcTokenStore(dataSource); ///使用Jdbctoken store
    }

    @Bean
    public ClientDetailsService clientDetails() {
        return new JdbcClientDetailsService(dataSource);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 配置客户端, 用于client认证
        clients.withClientDetails(clientDetails());
// 第一次使用的时候,需要配置客户端信息
//        clients.jdbc(dataSource)
//                .withClient("myClient")
//                .secret(new BCryptPasswordEncoder().encode("123456"))
//                .authorizedGrantTypes("refresh_token","client_credentials")//允许授权范围
//                .authorities("ROLE_ADMIN","ROLE_USER")//客户端可以使用的权限
//                .scopes( "read", "write")
//                .accessTokenValiditySeconds(7200)
//                .refreshTokenValiditySeconds(7200);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore())
                .authenticationManager(authenticationManager)
                .userDetailsService(myUserService);//必须设置 UserDetailsService 否则刷新token 时会报错

    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients();//允许表单登录

    }

}

如果说是其他模式:

.authorizedGrantTypes("authorization_code","client_credentials","password","refresh_token")//授权方式

配置资源服务器:

/**
 * @Author dw
 * @ClassName ResourceServerConfig
 * @Description 配置资源器
 * @Date 2020/4/20 15:03
 * @Version 1.0
 */
@Configuration
@EnableResourceServer //这个类表明了此应用是OAuth2 的资源服务器,此处主要指定了受资源服务器保护的资源链接
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {

        http.csrf().disable()//禁用了 csrf 功能
                .authorizeRequests()//限定签名成功的请求
                .antMatchers("/decision/**","/govern/**").hasAnyRole("USER","ADMIN")
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/test/**")
                .authenticated()//必须认证过后才可以访问
                .anyRequest().permitAll()//其他没有限定的请求,允许随意访问
                .and().anonymous();//对于没有配置权限的其他请求允许匿名访问
    }

}

配置springSecurity:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserServiceImpl myUserService;

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

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 配置用户签名服务 主要是user-details 机制,
     *
     * @param auth 签名管理器构造器,用于构建用户具体权限控制
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserService)
                .passwordEncoder(passwordEncoder());
    }


    /**
     * 用来配置拦截保护的请求
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //不拦截 oauth 开放的资源
        http.csrf().disable();

        http.requestMatchers()//使HttpSecurity接收以"/login/","/oauth/"开头请求。
                .antMatchers("/oauth/**", "/login/**", "/logout/**")
                .and()
                .authorizeRequests()
                .antMatchers("/oauth/**").authenticated()
                .and()
                .formLogin();
    }

}

定义MyUserDetailService

/**
 * @Author dw
 * @ClassName UserService
 * @Description
 * @Date 2020/4/14 13:26
 * @Version 1.0
 */
@Service
public class MyUserServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapperDao userMapperDao;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapperDao.loadUserByUsername(username);
        // 由于我这儿数据库的密码没有加密,所以手动加密
        String encodePassword = passwordEncoder.encode(user.getPassword());
        System.out.println("加密后的密码:" + encodePassword);
        user.setPassword(encodePassword);
        if (user == null) {
            throw new UsernameNotFoundException("账户不存在!");
        }
        List<Role> userRoles = userMapperDao.getUserRolesByUid(user.getId());
        user.setUserRoles(userRoles);
        return user;
    }

}

定义User实体:

/**
 * (User)实体类
 *
 * @author makejava
 * @since 2020-04-14 13:10:12
 */
public class User implements UserDetails {

    private Integer id;
    
    private String userName;
    
    private String password;
    
    private boolean enable;
    
    private boolean locked;

    private List<Role> userRoles;


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Role role : userRoles) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return authorities;
    }

    @Override
    public String getUsername() {
        return userName;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return !locked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return enable;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPassword() {
        return password;
    }

    public boolean isEnable() {
        return enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public boolean isLocked() {
        return locked;
    }

    public void setLocked(boolean locked) {
        this.locked = locked;
    }

    public List<Role> getUserRoles() {
        return userRoles;
    }

    public void setUserRoles(List<Role> userRoles) {
        this.userRoles = userRoles;
    }
}

Role:

public class Role implements Serializable {
    private static final long serialVersionUID = 825384782616737527L;
    
    private Integer id;
    
    private String name;
    
    private String description;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

}
View Code

userRole:

public class UserRole implements Serializable {
    private static final long serialVersionUID = -33173102844662867L;
    
    private Integer id;
    
    private Integer userId;
    
    private Integer roleId;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

}
View Code

定义userMapper:

/**
 * @Author dw
 * @ClassName UserMapperDao
 * @Description
 * @Date 2020/4/14 13:11
 * @Version 1.0
 */
@Repository
public interface UserMapperDao {

  public User loadUserByUsername(String userName);

  public List<Role> getUserRolesByUid(Integer id);


}

获取token:

(A) 客户端向认证服务器进行身份认证,并要求一个访问令牌。请求的参数如下:
  • response_type:表示授权类型,必选项,此处的值固定为"client_credentials"
  • client_id:表示客户端的ID,必选项
  • client_secret:客户端的密码,可选项
  • scope:表示申请的权限范围,可选项
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=s6BhdRkqt3
&client_secret=123456&scope=test

 返回:

 

猜你喜欢

转载自www.cnblogs.com/dw3306/p/12745900.html
今日推荐