springcloudsecurity+oauth+redis(jdbc)+mybatisplus实现授权认证+资源认证(基于授权码模式)

springcloudsecurity+oauth+redis+mybatisplus实现授权认证(基于授权码模式)
理解OAuth 2.0:http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
1.技术栈;springboot+springcloudsecurity+mybatisplus

交互过程

oAuth 在 “客户端” 与 “服务提供商” 之间,设置了一个授权层(authorization layer)。“客户端” 不能直接登录 “服务提供商”,只能登录授权层,以此将用户与客户端区分开来。“客户端” 登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。“客户端” 登录授权层以后,“服务提供商” 根据令牌的权限范围和有效期,向 “客户端” 开放用户储存的资料
在这里插入图片描述

认证服务器

1.AuthorizationServerConfiguration.java

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private DataSource dataSource;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

   /* @Bean
      public TokenStore tokenStore() {
          // 基于 JDBC 实现,令牌保存到数据库
          return new JdbcTokenStore(dataSource);
      }*/
    //令牌保存到Redis*/
    @Bean
    public TokenStore tokenStore() {
        RedisTokenStore redisToken = new RedisTokenStore(redisConnectionFactory);
        return redisToken;
    }
    @Bean
    public ClientDetailsService jdbcClientDetails() {
        // 基于 JDBC 实现,需要事先在数据库配置客户端信息
        return new JdbcClientDetailsService(dataSource);
    }

    /**
     * 在此处定义认证管理,即系统或者集群中的用户以及 Token的存儲方式, 定义授权、token终端、token服务
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 自定义token生成方式,增加用户登录名等信息
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(new MyTokenEnhancer()));
        //通过注入authenticationManager 会自动开启password grants
        // 设置令牌
        endpoints
            .authenticationManager(authenticationManager)
            .tokenStore(tokenStore()).tokenEnhancer(tokenEnhancerChain)
            .userDetailsService(userDetailsService)
            .exceptionTranslator(e -> {
                if (e instanceof OAuth2Exception) {
                    OAuth2Exception oAuth2Exception = (OAuth2Exception) e;
                    return ResponseEntity
                        .status(HttpStatus.UNAUTHORIZED)
                        .body(new CustomOauthException(oAuth2Exception.getMessage()));
                } else {
                    throw e;
                }
            });
    }

    /**
     * 支持刷新token
     *
     * @return DefaultTokenServices
     */
    @Primary
    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setAccessTokenValiditySeconds(60000);
        defaultTokenServices.setRefreshTokenValiditySeconds(604800);
        defaultTokenServices.setSupportRefreshToken(true);
        defaultTokenServices.setReuseRefreshToken(false);
        defaultTokenServices.setTokenStore(tokenStore());
        return defaultTokenServices;
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 读取客户端配置
        clients.withClientDetails(jdbcClientDetails());
    }
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {

        //允许表单认证
        oauthServer.allowFormAuthenticationForClients();

        //允许 check_token 访问
        oauthServer.checkTokenAccess("permitAll()");
    }

2.WebSecurityConfiguration.java


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)//全局方法拦截
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        // 设置默认的加密方式
        return new BCryptPasswordEncoder();
    }

   @Bean
    public DaoAuthenticationProvider authProvider() {
        final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 使用自定义认证与授权
        auth
            .authenticationProvider(authProvider())
            .userDetailsService(userDetailsService())
            .passwordEncoder(passwordEncoder());
    }
    @Override
    public void configure(WebSecurity web) throws Exception {
        // 将 check_token 暴露出去,否则资源服务器访问时报 403 错误
        web.ignoring().antMatchers("/oauth/check_token");
    }


    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    /**
     * 通过HttpSecurity实现Security的自定义过滤配置
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {

        http.csrf().disable();
        http
            .requestMatchers().antMatchers("/oauth/**","/login/**","/logout/**")
            .and()
            .authorizeRequests()
            .antMatchers("/oauth/**").authenticated()
            .and()
            .formLogin().permitAll(); //新增login form支持用户登录及授权
    }
}

3.UserDetailsServiceImpl.java

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private TbPermissionService tbPermissionService;
    @Autowired
    private TbUserService tbUserService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        TbUser tbUser = tbUserService.queryTbUserByUserName(username);
        List<GrantedAuthority> grantedAuthorities = new ArrayList();
        if (StringUtils.isEmpty(tbUser)) {
            throw new UsernameNotFoundException("很抱歉,找不到用户名为" + username + "的用户!");
        }

        if (tbUser != null) {
            List<TbPermission> tbPermissions = tbPermissionService
                .queryPermissionByUserId(tbUser.getId());
            tbPermissions.forEach(tbPermission -> {
                if (tbPermission != null && tbPermission.getEnname() != null) {
                    GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(tbPermission.getEnname());
                    grantedAuthorities.add(grantedAuthority);
                }
            });
        }
        /**
         * 若只是认证,要去掉grantedAuthorities换成 Collections.emptyList()
         * 若是要授权,要加上
         */
        CustomUserPrincipal userDetail = new CustomUserPrincipal(tbUser.getUsername(), tbUser.getPassword(), grantedAuthorities);
        userDetail.setTbUser(tbUser);
        return userDetail;
    }

资源服务器

4.ResourceServerConfiguration.java

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {


    @Autowired
    private RedisConnectionFactory redisConnectionFactory;


    /*@Autowired
    private DataSource dataSource;
     @Bean
      public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
      }*/
    //
    @Bean
    public TokenStore tokenStore() {
        return new RedisTokenStore(redisConnectionFactory);
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        //resourceId 推荐每个 受保护的资源都提供一下 ,可以供 auth服务对资源进行一个认证
        resources.resourceId("ls_resource").tokenStore(tokenStore());
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
            .exceptionHandling()
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
            // 以下为配置所需保护的资源路径及权限,需要与认证服务器配置的授权部分对应
            .antMatchers("/contents").hasAuthority("SystemContent")
            .antMatchers("/view/**").hasAuthority("SystemContentView")
            .antMatchers("/insert/**").hasAuthority("SystemContentInsert")
            .antMatchers("/update/**").hasAuthority("SystemContentUpdate")
            .antMatchers("/delete/**").hasAuthority("SystemContentDelete");
    }
}

5.localhost:8080/oauth/authorize?client_id=client&response_type=code获取code
用户名:admin 密码:123456
在这里插入图片描述

6.获取token
在这里插入图片描述
7.访问资源
在这里插入图片描述
8.其他代码请见GitHub:https://github.com/smileLs66/springcloud/tree/master/auth-oauth2
参考:
https://www.funtl.com/zh/guide/Spring-Security-oAuth2.html
https://www.cnblogs.com/cjsblog/p/10548022.html
阮一峰的jwt入门教程:http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
阮一峰理解OAuth2.0: https://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html

posted @ 2019-07-20 13:56  smileLS66  阅读(152)  评论(0编辑  收藏  举报