spring boot 2.0 权限进阶 spring security + oauth2

  最近在学习spring boot 权限相关模块,网上也有很多关于这方面的教程说明,发现需要搜索很多资料才能把spring security + oauth2 demoi搭建起来。希望这篇文章能帮助许多正在学习的同学们解决问题。

  如果有同学只想整合 spring security 请到我的github 下载代码学习:https://github.com/ltztao/test-security


Maven依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 -->
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.5.RELEASE</version>
        </dependency>

注意:spring-security-oauth2>=2.3.0,<2.3.4中检测到已知的高严重性安全漏洞,请合理引用依赖

认证服务安全配置
/**
 * 认证服务器配置
 * @author ltzhang
 * @time 2019-11-28
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    /**
     * 注入权限验证控制器 来支持 password grant type
     */
    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 注入userDetailsService,开启refresh_token需要用到
     */
    @Autowired
    private MyUserDetailsService userDetailsService;

    /**
     * 数据源
     */
    @Autowired
    private DataSource dataSource;
    @Autowired
    private RedisConnectionFactory connectionFactory;

    @Autowired
    private MyOAuth2WebResponseExceptionTranslator webResponseExceptionTranslator;


    @Bean
    public TokenStore tokenStore() {
        return new RedisTokenStore( connectionFactory );
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }


    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //开启密码授权类型
        endpoints.authenticationManager(authenticationManager);
        //配置token存储方式
        endpoints.tokenStore(tokenStore());
        //自定义登录或者鉴权失败时的返回信息
        endpoints.exceptionTranslator(webResponseExceptionTranslator);
        //要使用refresh_token的话,需要额外配置userDetailsService
        endpoints.userDetailsService( userDetailsService );
        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
    }
}

此处token信息都存放在redis中,client信息存放在数据库中(如果要配置在内存中,请自行百度
大家如果想讲token信息保存在数据请自行做相关修改,oauth2 相关数据表 https://github.com/ltztao/spring-security-oauth2/blob/master/init-oauth2.sql

-- 由于当前token存放在redis中没使用jdbc且只有令牌存放在数据库中达到可管理,故只要创建oauth_client_details即可
-- 如若token存放数据库中则需要创建以下所有表

CREATE TABLE `oauth_client_details` (
  `client_id` varchar(128) NOT NULL,
  `resource_ids` varchar(256) DEFAULT NULL,
  `client_secret` varchar(256) DEFAULT NULL,
  `scope` varchar(256) DEFAULT NULL,
  `authorized_grant_types` varchar(256) DEFAULT NULL,
  `web_server_redirect_uri` varchar(256) DEFAULT NULL,
  `authorities` varchar(256) DEFAULT NULL,
  `access_token_validity` int(11) DEFAULT NULL,
  `refresh_token_validity` int(11) DEFAULT NULL,
  `additional_information` varchar(4096) DEFAULT NULL,
  `autoapprove` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

数据库字段说明 http://andaily.com/spring-oauth-server/db_table_description.html
资源服务配置
/**
 * 资源提供端的配置
 * @author ltzhang
 * @time 2019-11-28
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    private RestAuthAccessDeniedHandler deniedHandler;

    @Autowired
    private MyAuthenticationEntryPoint authenticationEntryPoint;
    /**
     * 这里设置需要token验证的url
     * 可以在WebSecurityConfigurerAdapter中排除掉,
     * 对于相同的url,如果二者都配置了验证
     * 则优先进入ResourceServerConfigurerAdapter,进行token验证。而不会进行
     * WebSecurityConfigurerAdapter 的 basic auth或表单认证。
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.requestMatchers().antMatchers("/hi")
                .and()
                .authorizeRequests()
                .antMatchers("/hi")
                .authenticated();
        //需要的时候创建session,支持从session中获取认证信息,ResourceServerConfiguration中
        //session创建策略是stateless不使用,这里其覆盖配置可创建session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.authenticationEntryPoint(authenticationEntryPoint)
                .accessDeniedHandler(deniedHandler);
        //此处是关键,默认stateless=true,只支持access_token形式,
        // OAuth2客户端连接需要使用session,所以需要设置成false以支持session授权
        resources.stateless(false);
    }
    
}

Web安全配置


@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class BrowerSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private MyAuthenticationSuccessHandler successHandler;
    @Autowired
    private MyAuthenticationFailHandler failHandler;

    @Autowired
    private AjaxSessionInformationExpiredStrategy expiredStrategy;

    @Autowired
    private SecurityAuthenticationProvider provider;

    @Autowired
    private RestAuthAccessDeniedHandler deniedHandler;
    @Autowired
    private MyLogoutSuccessHandler      logoutSuccessHandler;
    @Autowired
    private MyAuthenticationEntryPoint  entryPoint;

    /**
     * 如若需从数据库动态判断权限则实现 AccessDecisionManager
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //"/login"不进行权限验证
                .antMatchers("/login").permitAll()
//                .antMatchers("/favicon.ico").permitAll()
                .and()
                .authorizeRequests()
                .anyRequest().authenticated()   //其他的需要登陆后才能访问
                .and()
                .formLogin()
                //loginProcessingUrl用于指定前后端分离的时候调用后台登录接口的名称
                .loginProcessingUrl("/login")
                .usernameParameter("username")
                .passwordParameter("password")
                .successHandler(successHandler)
                .failureHandler(failHandler).permitAll()
                .and()
                //loginProcessingUrl用于指定前后端分离的时候调用后台注销接口的名称
                .logout().logoutUrl("/logout")
                .invalidateHttpSession(true)
                .logoutSuccessHandler(logoutSuccessHandler)

                .and()
                //配置没有权限的自定义处理类
                .exceptionHandling()
                .accessDeniedHandler(deniedHandler)
                .authenticationEntryPoint(entryPoint); // 自定义处理未登录

        // 设置跨域问题
        http.cors().and().csrf().disable();

        http.sessionManagement()
                .maximumSessions(1)  // 同一个账号只能在一个地方登陆
                .expiredSessionStrategy(expiredStrategy);
    }

    /**
     * 自定义验证逻辑
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth){
        auth.authenticationProvider(provider);
    }

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

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

}

当前demo中如:token过期,登录失败,登录成功...都处理返回json,如果其它需求请自行处理。


测试

image.png

完整的代码下载链接:https://github.com/ltztao/spring-security-oauth2

参考资料均来自网上

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容