SpringBoot的Security和OAuth2的使用

news/2024/10/2 12:30:10

创建项目

先创建一个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*/@BeanUserDetailsService 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*/@BeanWebSecurityCustomizer webSecurityCustomizer() {return new WebSecurityCustomizer() {@Overridepublic 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@SneakyThrowsprotected 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@SneakyThrowsprotected void configure(HttpSecurity http) {http.httpBasic().and()//禁用跨站请求伪造(CSRF)保护。.csrf().disable().authorizeRequests().anyRequest().fullyAuthenticated();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("kiba518").password(passwordEncoder().encode("123")).authorities(new ArrayList<>(0));}@Beanpublic 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 {@Autowiredprivate PasswordEncoder passwordEncoder;@Autowiredprivate AuthenticationManager authenticationManager;@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {//允许表单提交security.allowFormAuthenticationForClients().checkTokenAccess("isAuthenticated()");}@Overridepublic 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"); //作用域}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.authenticationManager(authenticationManager);}}

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

@Beanpublic 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 {@Beanpublic 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;}@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {resources.stateless(true);}@Overridepublic 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/44802.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Spring容器系列-启动原理(下)

Spring容器系列-启动原理(下)从上一篇文章《Spring容器系列-启动原理(上)》中,介绍了Spring容器初始化的核心方法refresh()的整体情况。这篇文章来详细展开介绍。其中比较重要的方法会标记上***一、refresh中的12个方法1. prepareRefresh主要作用:记录下容器启动时间,标…

云原生打包工具-Buildpacks

云原生正在吞并软件世界,容器改变了传统的应用开发模式,如今研发人员不仅要构建应用,还要使用 Dockerfile 来完成应用的容器化,将应用及其依赖关系打包,从而获得更可靠的产品,提高研发效率。 随着项目的迭代,达到一定的规模后,就需要运维团队和研发团队之间相互协作。运…

uniapp计算字符串宽度

最近手机端聊天功能,想实现气泡框,根据输入的文本,背景颜色自动适应宽度本来呢,只需要在文本外包裹一个标签,不指定宽度,就能实现,但是nvue中文本只能写在text中,来实现字体大小颜色的设置,在使用text标签后就会导致文本框宽度直接就是最长 第一种方法: 所以需要计算…

服务器从hugging face下载数据集失败

问题 服务器没法下载外网数据导致hugging face的load_dataset下载失败。 解决方法 没找到load_dataset的源代码,想办法本地下载并上传到服务器,服务器从本地使用数据集。 示例:dataset = load_dataset("mit-han-lab/pile-val-backup", split="validation&quo…

微信小程序开发云环境,使用云函数实现通用印刷体识别, openapi.ocr.printedText

1.创建新项目时,要选中云开发,APPID不能使用测试号2.微信开发者工具,点击云开发,进行注册使用,刚开始是可以试用30天,之后是需要付费的。3.npm install --save wx-server-sdk@latest 终端安装此命令 https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/img…

ABC353F 分讨

回来补补题。 分析: 我先考虑 \(k\) 很大的时候,大块和大块间的移动,我们不得不尽量避免小块:我们容易发现这样时是最优的,可以发现就是在斜着走,也就是典型的切比雪夫距离。斜着走一次需要经过两条边,所以花费是两倍的切比雪夫距离。 要是起点和终点不在大块上呢? 首先…

数据可视化是如何在智慧水利中应用的?

数据可视化是如何在智慧水利中应用的?在现代水利管理中,面对复杂的水资源数据和动态变化的水文情况,数据可视化技术通过将繁杂的数据转化为直观、易理解的图表和图形,极大地提升了水利管理的效率和决策的科学性。智慧水利利用数据可视化技术,实现了对水资源的全面监控、精…

CloudEvents-云原生事件规范

简介 CloudEvents 是一种定义事件数据在云端应用之间如何交付的规范,这是由 Cloud Native Computing Foundation(CNCF)的 Serverless 工作小组开发的。通过提供统一的事件格式,CloudEvents 旨在简化跨服务、平台和供应商的事件交付。 CloudEvents位于CNCF全景图的”流和消息…