SpringBoot OAuth2.0 认证授权(密码模式)

SpringBoot OAuth2.0 认证授权(密码模式)

SpringBoot 整合 SpringSecurity,token 落地,前后端分离接口安全。

SpringBoot 环境搭建和入门:Spring Boot 2.x 快速入门

导入 mysql 脚本

包含用户表,oauth2.0 数据脚本

https://gitee.com/shizidada/moose-resource/blob/master/moose-security.sql

全部 : https://gitee.com/shizidada/moose/blob/master/src/main/resources/moose.sql

导入依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-securityartifactId>
dependency>

<dependency>
    <groupId>org.springframework.security.oauth.bootgroupId>
    <artifactId>spring-security-oauth2-autoconfigureartifactId>
    <version>2.2.5.RELEASEversion>
dependency>

创建 WebSecurity 配置文件

@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true)
public class MooseWebSecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Bean
  @Override
  public UserDetailsService userDetailsServiceBean() throws Exception {
    return new UserDetailsServiceImpl();
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsServiceBean());
  }

  /**
   * 用于支持 password 模式 密码模式需要 AuthenticationManager 支持
   * password 模式一点要加上这个
   * @throws Exception
   */
  @Bean
  @Override
  public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
  }
}

创建 AuthorizationServer 配置

/**
 * 资源认证配置
 * 

* Description: *

* * @author taohua * @version v1.0.0 * @see com.moose.operator.web.security.configure *

* [/oauth/authorize] [/oauth/token] [/oauth/check_token] * [/oauth/confirm_access] [/oauth/token_key] [/oauth/error] */ @Configuration @EnableAuthorizationServer public class MooseAuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { /** * 注入用于支持 password 模式 */ @Resource private AuthenticationManager authenticationManager; @Resource private AccountService accountService; /** * 需要加上,避免循环依赖问题 */ @Lazy @Resource(name = "userDetailsServiceBean") private UserDetailsService userDetailsService; @Resource private DataSource dataSource; @Resource private WebResponseExceptionTranslator customOAuth2ResponseExceptionTranslator; /** * 设置用户密码的加密方式 */ @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } /** * Token 持久化 * * @return TokenStore */ @Bean public TokenStore tokenStore() { // 基于 JDBC 实现,令牌保存到数据库 return new JdbcTokenStore(dataSource); } /** * A service that provides the details about an OAuth2 client. * * @return ClientDetailsService *

* 基于 JDBC 实现,需要事先在数据库配置客户端信息 */ @Bean public ClientDetailsService jdbcClientDetailsService() { return new JdbcClientDetailsService(dataSource); } /** * Authorization Server endpoints. * * @throws Exception */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .tokenStore(tokenStore()) .exceptionTranslator(customOAuth2ResponseExceptionTranslator); // 用于支持密码模式 endpoints.authenticationManager(authenticationManager); } /** * 授权服务安全配置 * * @throws Exception */ @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.passwordEncoder(passwordEncoder()); /** * 对于端点可以匿名访问 * [/oauth/authorize] [/oauth/token] [/oauth/check_token] * [/oauth/confirm_access] [/oauth/token_key] [/oauth/error] */ oauthServer .tokenKeyAccess("permitAll()") .checkTokenAccess("permitAll()") .allowFormAuthenticationForClients(); } /** * 授权客户端配置 * * @throws Exception */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // 客户端配置 clients.withClientDetails(jdbcClientDetailsService()); } }

创建 ResourceServer配置

@Configuration
@EnableResourceServer
public class MooseResourceServerConfiguration extends ResourceServerConfigurerAdapter {

  /**
   * 保存匿名访问的 url,表示可以不需要权限直接可以访问
   */
  private final List<String> anonymousAntPatterns = new ArrayList<>();

  @Resource
  private MooseAuthenticationFailureHandler mooseAuthenticationFailureHandler;

  @Resource
  private MooseAccessDeniedHandler mooseAccessDeniedHandler;

  @Resource
  private MooseAuthenticationEntryPoint mooseAuthenticationEntryPoint;

  /**
  * 需要加上,避免循环依赖问题
  */
  @Lazy
  @Resource
  private TokenStore tokenStore;

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

    http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll();

    // 对登录注册要允许匿名访问
    for (String pattern : anonymousAntPatterns) {
      http.authorizeRequests().antMatchers(pattern).permitAll();
    }

    // 禁用 session
    http
        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
        .authorizeRequests().anyRequest().authenticated().and()
        .cors().and()
        .csrf().disable();
  }

  @Override
  public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    // 配置资源 ID,需要在 oauth_client_details 表中添加,详细查看数据库脚本
    resources.resourceId("app-resources").stateless(true);
    resources.accessDeniedHandler(mooseAccessDeniedHandler);
    resources.authenticationEntryPoint(mooseAuthenticationEntryPoint);
  }
}

自定义认证 UserDetailsService

  • 需要继承 UserDetailsService 类
  • 重写 loadUserByUsername 方法,通过这个方法得到访问接口传递的用户名
  • 根据用户名进行用户名密码校验和权限校验
  • 查询出来的用户信息构建 UserDetails 对应进行返回传递

具体可以查看 https://gitee.com/shizidada/moose/blob/master/src/main/java/com/moose/operator/web/service/impl/UserDetailsServiceImpl.java

public class UserDetailsServiceImpl implements UserDetailsService {

  @Resource
  private AccountServiceImpl accountService;

  @Resource
  private PasswordServiceImpl passwordService;

  /**
   * Spring Security
   *
   * @param accountName 账号
   * @return 是否成功
   * @throws UsernameNotFoundException 用户名密码异常
   */
  @Override
  public UserDetails loadUserByUsername(String accountName)
      throws UsernameNotFoundException {

    if (StringUtils.isEmpty(accountName)) {
      throw new BusinessException(ResultCode.ACCOUNT_IS_EMPTY);
    }

    AccountDTO accountDTO = accountService.getByAccountName(accountName);
    if (ObjectUtils.isEmpty(accountDTO)) {
      throw new BusinessException(ResultCode.ACCOUNT_OR_PASSWORD_ERROR);
    }

    // TODO: 禁用账号如何防止多次请求,访问数据库 ???

    PasswordDTO passwordDTO = passwordService.getByAccountId(accountDTO.getAccountId());
    if (ObjectUtils.isEmpty(passwordDTO)) {
      throw new BusinessException(ResultCode.ACCOUNT_OR_PASSWORD_ERROR);
    }

    // TODO: 角色、权限集合
    List<GrantedAuthority> grantedAuthorities = new ArrayList();
    grantedAuthorities.add(new SimpleGrantedAuthority("USER"));
    return new MooseUserDetails(accountDTO, passwordDTO, grantedAuthorities);
  }
}

自定义验证,需要用到 Bean

  • MooseUserDetails 构建用户和权限信息

    • https://gitee.com/shizidada/moose/blob/master/src/main/java/com/moose/operator/web/security/component/MooseUserDetails.java
  • MooseAccessDeniedHandler 用来解决认证过的用户访问无权限资源时的异常

    • https://gitee.com/shizidada/moose/blob/master/src/main/java/com/moose/operator/web/security/component/MooseAccessDeniedHandler.java
  • MooseAuthenticationEntryPoint 用来解决匿名用户访问无权限资源时的异常

    • https://gitee.com/shizidada/moose/blob/master/src/main/java/com/moose/operator/web/security/component/MooseAuthenticationEntryPoint.java
  • MooseAuthenticationExceptionSerializer 自定义授权错误解析器

    • https://gitee.com/shizidada/moose/blob/master/src/main/java/com/moose/operator/web/security/component/MooseAuthenticationExceptionSerializer.java
  • MooseAuthenticationFailureHandler 自定义授权失败或错误

    • https://gitee.com/shizidada/moose/blob/master/src/main/java/com/moose/operator/web/security/component/MooseAuthenticationFailureHandler.java
  • MooseAuthenticationResponseExceptionTranslator 自定义授权错误传输器

    • https://gitee.com/shizidada/moose/blob/master/src/main/java/com/moose/operator/web/security/component/MooseAuthenticationResponseExceptionTranslator.java

测试

  • 向 oauth_client_details 表中 authorized_grant_types 字段添加 password 模式

  • 向数据库添加测试用户, password 需要用 BCryptPasswordEncoder 进行加密
# t_account 表
insert  into `t_account`(`account_id`,`account_name`,`status`,`phone`,`create_time`,`update_time`) values ('785919644115668992','江景','1','1537031501','2020-12-08 17:24:04','2020-12-08 17:24:04');

# t_password 表
insert  into `t_password`(`password_id`,`account_id`,`password`,`create_time`,`update_time`) values ('785919644115668993','785919644115668992','$2a$10$Ll17vDGG3DoABuy9L026KeqjAasYknMXR3cHmxiTyfDl9dS8RtGbi','2020-12-08 17:24:04','2020-12-08 17:24:04');

代码地址: https://gitee.com/shizidada/moose

关注公众号 「全栈技术部」,不断学习更多有趣的技术知识。

SpringBoot OAuth2.0 认证授权(密码模式)_第1张图片

你可能感兴趣的:(SpringBoot,java,spring,boot)