SpringBoot的Security和OAuth2的使用

springboot,security,oauth2 · 浏览次数 : 0

小编点评

本文介绍了如何使用Spring Boot 2.4.2创建一个安全的项目,包括创建Spring项目、编写pom文件、引入Spring Boot Starter Security以及配置安全相关的bean。同时,还讨论了如何通过自定义WebSecurityConfigurerAdapter类来实现用户密码的配置和授权操作。 1. **创建Spring项目**:首先,创建一个新的Spring项目,选择Spring Boot 2.4.2版本,并添加必要的依赖。 2. **编写pom.xml文件**:在pom.xml文件中,引入Spring Boot Starter Security依赖,以便使用Spring Boot的安全功能。 3. **配置安全相关的bean**:通过编写配置类,如SecurityConfig,注册用户并配置WebSecurityCustomizer以实现免登录访问。 4. **自定义WebSecurityConfigurerAdapter类**:创建一个自定义的WebSecurityConfigurerAdapter子类,用于修改授权操作,例如通过配置AuthenticationManagerBuilder来创建用户。 5. **实现OAuth2授权服务器**:介绍如何使用Spring Security OAuth2来实现授权服务器的功能,包括创建AuthorizationConfig配置类和配置远程TokenServices。 6. **实现资源服务**:创建一个资源服务项目,添加必要的依赖,并配置RemoteTokenServices以实现token检查功能。 总的来说,本文详细介绍了如何使用Spring Boot 2.4.2搭建一个安全的项目,包括用户管理、授权操作和OAuth2授权服务器的实现。通过自定义配置类和OAuth2授权服务器,可以实现灵活的安全机制,满足不同项目的安全需求。

正文

创建项目

先创建一个spring项目。

然后编写pom文件如下,引入spring-boot-starter-security,我这里使用的spring boot是2.4.2,这里使用使用spring-boot-dependencies,在这里就能找到对应的security的包。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>app-kiba-security</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>app-kiba-security</name>
    <description>app-kiba-security</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.4.2</spring-boot.version>
    </properties>
    <dependencies>
        <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>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>


    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.kiba.appkibasecurity.AppKibaSecurityApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

然后访问创建项目时默认生成的接口:http://127.0.0.1:8080/user/123/roles/222,得到如下界面。

image

这是相当于,在我们的接口请求的前面做了一个拦截,类似filter,拦截后,跳转到了一个界面,让我们输入账号密码。这里,我由于没有设置账号密码,所以登录不进去。

设置访问一

下面设置一个账号密码,并且设置hello接口可以直接访问,设置很简单,就是注入两个bean,InMemoryUserDetailsManager和WebSecurityCustomizer,代码如下:

@Configuration
public class SecurityConfig   {
    /**
     * 注册用户,这里用户是在内存中的
     *  {noop}表示“无操作”(No Operation)密码编码。
     * @return
     */
    @Bean
    UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager users = new InMemoryUserDetailsManager();
        users.createUser(User.withUsername("kiba").password("{noop}123").roles("admin").build()); 
        return users;
    }

    /**
     * 让hello可以不用登录,就可以直接访问,例如:http://127.0.0.1:8080/hello?name=kiba就可以直接访问
     * @return
     */
    @Bean
    WebSecurityCustomizer webSecurityCustomizer() {
        return new WebSecurityCustomizer() {
            @Override
            public void customize(WebSecurity web) {
                web.ignoring().antMatchers("/hello");
            }
        };
    }

}

现在我们访问http://127.0.0.1:8080/user/123/roles/222,进入到登录页面,输入kiba/123就可以查看接口执行的结果了。

http://127.0.0.1:8080/hello?name=kiba就无需登录,可以直接访问。

登录一次,其他接口就可以自由访问了

控制请求

现在,增加一个类SecurityAdapter,继承自WebSecurityConfigurerAdapter。然后重写他的configure方法

@Configuration
@AllArgsConstructor
public class SecurityAdapter extends WebSecurityConfigurerAdapter {

    /**
     * authenticated():用户需要通过用户名/密码登录,记住我功能也可以(remember-me)。
     * fullyAuthenticated()用户需要通过用户名/密码登录,记住我功能不行。 
     */
    @Override
    @SneakyThrows
    protected void configure(HttpSecurity http) {
        http.httpBasic().and()
                //禁用跨站请求伪造(CSRF)保护。
                .csrf().disable()
                .authorizeRequests().anyRequest().fullyAuthenticated();
    } 

}

当使用,增加了SecurityAdapter后,我们重新请求http://127.0.0.1:8080/user/123/roles/222,得到界面如下:

image

可以看到,登录界面的样式被美化了。

设置访问二(推荐)

我们还可以使用第二种方法,来做用户密码的配置。

通过重写configure(AuthenticationManagerBuilder auth)函数,来创建用户,这种方式创建用户会将前面的bean-UserDetailsService给覆盖,即,用户只剩下这里创建的。

代码如下:

@Configuration
@AllArgsConstructor
public class SecurityAdapter extends WebSecurityConfigurerAdapter {

    /**
     * authenticated():用户需要通过用户名/密码登录,记住我功能也可以(remember-me)。
     * fullyAuthenticated()用户需要通过用户名/密码登录,记住我功能不行。
     */
    @Override
    @SneakyThrows
    protected void configure(HttpSecurity http) {
        http.httpBasic().and()
                //禁用跨站请求伪造(CSRF)保护。
                .csrf().disable()
                .authorizeRequests().anyRequest().fullyAuthenticated();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("kiba518")
                .password(passwordEncoder().encode("123"))
                .authorities(new ArrayList<>(0));
    }

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

}

这里的用户是写死的,用户是可以修改成读取数据库的信息的。

我们查看WebSecurityConfigurerAdapter的代码,可以看到他有注解@Order(100),数越大,执行越优先级越低,即,他的执行顺序是相对比较靠后的。

授权OAuth2

授权这个设计理念是这样,它是结合上面的security的操作,实现了一个普通的WebApp转换成授权服务器WebApp。

授权服务器转换思路

我们先了解一下security转授权服务器的思路。

1,在这个应用里,创建一个auth接口,然后任何人想访问这个接口,就都需要输入账户密码了。

2,我们这个auth接口的返回值是个code,然后我们的前端,或者其他调用接口的APP,就可以把这个code作为用户登录的token了,。

3,然后我们再做一个接口,接受一个token参数,可以验证token是否有效。

这样我们这个授权服务器的搭建思路就构建完成了。

但按这个思路,我们需要做很多操作,比如创建接口,缓存token等等,现在spring提供了一个Oauth2的包,他可以帮我们实现这些接口定义。

OAuth2的接口如下,可以自行研究。

/oauth/authorize:授权端点

/oauth/token:获取令牌端点

/oauth/confirm_access:用户确认授权提交端点

/oauth/error:授权服务错误信息端点

/oauth/check_token:用于资源服务访问的令牌解析端点

/oauth/token_key:提供公有密匙的端点,如果使用JWT令牌的话

实现授权服务器

现在我们实现一个授权服务器。

先添加OAuth2的引用。

 <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.4.0.RELEASE</version>
        </dependency>

然后增加配置文件AuthorizationConfig。

@Configuration
@EnableAuthorizationServer //开启授权服务
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //允许表单提交
        security.allowFormAuthenticationForClients()
                .checkTokenAccess("isAuthenticated()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client-kiba") //客户端唯一标识(client_id)
                .secret(passwordEncoder.encode("kiba518-123456")) //客户端的密码(client_secret),这里的密码应该是加密后的
                .authorizedGrantTypes("password") //授权模式标识,共4种模式[授权码(authorization-code)隐藏式(implicit) 密码式(password)客户端凭证(client credentials)]
                .scopes("read_scope"); //作用域

    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }

}

然后打开SecurityAdapter,增加一个bean,如下,目的是让上面的AuthorizationConfig里Autowired的authenticationManager可以实例化。

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

然后使用APIFox调用一下/oauth/token接口。

先选择auth,输入账号密码,这个账号密码就是AuthorizationConfig里配置的客户端id和密码。

image

这个数据在请求时,会进行base64编码,然后以http的header属性Authorization的值的模式传递,如下。

image

然后输入参数,参数里scope和grant_type要和AuthorizationConfig里定义的scopes和authorizedGrantTypes一样,如下。

image

请求后,得到结果,如上图。

我们得到"access_token": "19d37af2-6e13-49c3-bf19-30a738b56886"。

有了access_token后,我们的前端其实就已经可以进行各种骚操作了。

资源服务

这个是Oauth为我们提供的一项很好用的功能。

我们创建一个项目做为资源服务。

添加依赖,版本与上面相同。

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

然后编写资源配置,代码如下:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Bean
    public RemoteTokenServices remoteTokenServices() {
        final RemoteTokenServices tokenServices = new RemoteTokenServices();
        tokenServices.setClientId("client-kiba");
        tokenServices.setClientSecret("kiba518-123456");
        tokenServices.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");//这个接口是oauth自带的
        return tokenServices;
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.stateless(true);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        //session创建策略
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        //所有请求需要认证
        http.authorizeRequests().anyRequest().authenticated();
    }
}

因为添加了spring-boot-starter-security,所以,我们请求这个资源WebApp,就都需要输入账号密码。

但因为,我们配置了ResourceServerConfig,这里我们配置了远程token服务,设置的信息是我们上面创建授权服务的信息。所以,在访问这个WebApp时,我们提供token即可。

使用APIFOX测试,先添加auth的token,内容是来自于上面,/oauth/token的返回值access_token的值。

image

然后请求user接口,我这user接口没有参数,请求结果如下:

image

总结

这个授权服务挺好用的,就是配置太繁琐了,初学者不太好理解,而且功能太多,配置太闹心。

这个资源服务还是很贴心的,他提我们实现了,tokencheck的部分,但要注意的是,他这tokencheck是基于http请求的。

虽然Oath很好用,但,我还是觉得,这个认证部分自己写比较好,我们可以根据项目的需求,设计轻量级的授权认证。

比如,我们想减少http请求,把部分tokencheck缓存内存check,那使用oauth时,修改起来就会很头疼。如果是自己写的授权服务器,就不会有修改困难的问题。


注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!



若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!

https://www.cnblogs.com/kiba/p/18252859

与SpringBoot的Security和OAuth2的使用相似的内容:

SpringBoot的Security和OAuth2的使用

创建项目 先创建一个spring项目。 然后编写pom文件如下,引入spring-boot-starter-security,我这里使用的spring boot是2.4.2,这里使用使用spring-boot-dependencies,在这里就能找到对应的security的包。

SpringBoot实战:Spring Boot接入Security权限认证服务

引言 Spring Security 是一个功能强大且高度可定制的身份验证和访问控制的框架,提供了完善的认证机制和方法级的授权功能,是一个非常优秀的权限管理框架。其核心是一组过滤器链,不同的功能经由不同的过滤器。本文将通过一个案例将 Spring Security 整合到 SpringBoot中,要

Spring Boot 3.1中如何整合Spring Security和Keycloak

在今年2月14日的时候,Keycloak 团队宣布他们正在弃用大多数 Keycloak 适配器。其中包括Spring Security和Spring Boot的适配器,这意味着今后Keycloak团队将不再提供针对Spring Security和Spring Boot的集成方案。但是,如此强大的Ke

Spring Security 报:Encoded password does not look like BCrypt

SpringBoot 集成 Security 时,报 Encoded password does not look like BCrypt 原因:SecurityConfig 必须 Bean 的形式实例化 /** * 配置用户身份的configure()方法 * * @param auth * @t

Springboot简单功能示例-3 实现基本登录验证

博主尝试通过gitee的发行版,使用Springboot为基础框架,逐步整合JWT、JPA、VUE等常用功能项目。【本节完成】使用spring-security6.1.3,抛弃了WebSecurityConfigurerAdapter的配置方式,直接使用session实现基本的登录验证功能

SpringBoot SpringSecurity 介绍(基于内存的验证)

SpringBoot 集成 SpringSecurity + MySQL + JWT 附源码,废话不多直接盘 SpringBoot已经为用户采用默认配置,只需要引入pom依赖就能快速启动Spring Security。 目的:验证请求用户的身份,提供安全访问 优势:基于Spring,配置方便,减少大

从零开始学Spring Boot系列-集成Spring Security实现用户认证与授权

在Web应用程序中,安全性是一个至关重要的方面。Spring Security是Spring框架的一个子项目,用于提供安全访问控制的功能。通过集成Spring Security,我们可以轻松实现用户认证、授权、加密、会话管理等安全功能。本篇文章将指导大家从零开始,在Spring Boot项目中集成S

Springboot实现注解判断权限

今天记录一下使用springboot的注解来给方法加权限 避免了每个方法都需要大量的权限判断 超级方便√

Springboot下micrometer+prometheus+grafana进行JVM监控的操作过程

Springboot下micrometer+prometheus+grafana进行JVM监控的操作过程 背景 同事今天提交了一个补丁. 给基于Springboot的产品增加了micrometer等收集jvm信息的工具 但是这边springboot的版本比较高,导致有异常. 启动直接失败了. 晚上九

简单进行Springboot Beans归属模块单元的统计分析方法

简单进行Springboot Beans归属模块单元的统计分析方法 背景 基于Springboot的产品变的复杂之后 启动速度会越来越慢. 公司同事得出一个结论. beans 数量过多会导致启动速度逐渐变慢. 之前同事写过功能进行分析. 但是本着能不影响产品就不影响产品. 我想通过其他方式进行处理.