spring security oauth2.0搭建用户认证服务(一)

论坛 期权论坛 脚本     
已经匿名di用户   2022-3-21 23:40   2669   0

项目场景:

公司开发了APP,不过因为开发的周期比较短。因此很多的功能都不是很完善。

比如用户的登录流程。之前的登录流程是直接通过调用接口,后端校验账号密码。校验成功之后直接返回用户信息。前端缓存用户信息,然后请求接口的时候把用户信息通过请求的数据带过来,用以标识当前接口的请求人。

感觉这么做并不安全,后端只能够根据前端传递的userId来区分用户标识。因此我打算基于spring security oauth来重构一下用户的登录模块,写这篇文章来记录自己从零到有的搭建过程以及排坑日记


技术介绍:

什么是OAuth2

OAuth2是一个关于授权的开放标准,核心思路是通过各类认证手段(具体什么手段OAuth2不关心)认证用户身份,并颁发token(令牌),使得第三方应用可以使用该令牌在限定时间限定范围访问指定资源。主要涉及的RFC规范有RFC6749(整体授权框架),RFC6750(令牌使用),RFC6819(威胁模型)这几个,一般我们需要了解的就是RFC6749。获取令牌的方式主要有四种,分别是授权码模式简单模式密码模式客户端模式,如何获取token不在本篇文章的讨论范围,我们这里假定客户端已经通过某种方式获取到了access_token,想了解具体的oauth2授权步骤可以移步阮一峰老师的理解OAuth 2.0,里面有非常详细的说明。

而我这个项目使用的是密码模式(增强)以及自定义的手机验证码模式和第三方登录登录模式。


项目搭建:

最早开始我是阅读了《Spring Security实战》这本书,然后按照上面的步骤一步一步的来搭建.

首先第一步是引入依赖(idea构建项目就省略).

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
      <exclusions>
      <exclusion>
        <groupId>org.springframework.security.oauth</groupId>
        <artifactId>spring-security-oauth2</artifactId>
      </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework.security.oauth.boot</groupId>
      <artifactId>spring-security-oauth2-autoconfigure</artifactId>
      <version>2.0.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-oauth2-client</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-oauth2-core</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security.oauth</groupId>
      <artifactId>spring-security-oauth2</artifactId>
      <!-- 指明版本,解决redis存储出现的问题:java.lang.NoSuchMethodError: org.springframework.data.redis.connection.RedisConnection.set([B[B)V问题 -->
      <version>2.3.3.RELEASE</version>
    </dependency>

第二步配置相关信息

1.实现UserDetailsService和User

UserDetailsService在整个spring security相关重要,基本上所有的需要用户相关的都需要调用它。而User作为接口的返回值,如果你需要拓展它的话也可通过继承User。代码如下
@Slf4j
@Service
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    protected PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        log.info("认证开始,userName = {}",username);
        // 获取用户信息以及角色省略,自己实现.
        if (appUser == null)
            throw new UsernameNotFoundException("用户信息查询异常.");
        // 添加角色信息
        Set<String> dbAuthSet = new HashSet<>();
        dbAuthSet.add("ROLE_admin");
        Collection<? extends GrantedAuthority> authorities
                = AuthorityUtils.createAuthorityList(dbAuthSet.toArray(new String[0]));
        
         //返回myUser的对象,密码需要passwordEncoder进行加密.以后后续校验的时候是加密之后的密码
        return new MyUser(username,passwordEncoder.encode(password),userId,authorities);
    }

}
/**
 * 后续可以根据业务需求,添加相应的字段内容
 */
public class MyUser extends User {

    private static final long serialVersionUID = 1L;

    @Getter
    private Integer userId;

    public MyUser(String username, String password,Integer userId,
                  Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
        this.userId = userId;
    }

}

2. 配置WebSecurityConfigurerAdapter

WebSecurityConfigurerAdapter是spring security的适配器, 在配置的时候,需要我们自己写个配置类去继承他,然后编写自己所特殊需要的配置,代码如下:
/**
 * 登录验证
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/resources/**", "/signup", "/about").permitAll()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .usernameParameter("username")
                .passwordParameter("password")
                .failureForwardUrl("/login?error")
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/index")
                .permitAll()
                .and()
            .httpBasic()
                .disable();
    }
    
    @Override
    @SneakyThrows
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers("/css/**", "/js/**","/websocket/**", "/index.html",
                "/img/**", "/fonts/**", "/favicon.ico");
        
    }    
        

}

完成上面三个配置,哪么就已经配置好spring security了。不过完成这些并不够,因为spring security 只是将会话信息放到session中,如果会话关闭的话,哪么用户就需要重新登录,这肯定不满足我们的需求。因此我们就需要在进一步扩展.

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:81
帖子:4969
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP