编辑
2023-07-06
linux
00

当两台服务器之间需要传输文件时,使用FTP软件(FileZilla)就很麻烦,需要一台中转机器。

使用scp则能解决此问题,一般Linux自带scp。

scp 命令介绍

scp 本地用户名@IP地址:文件名1 远程用户名@IP地址:文件名2

[本地用户名@IP地址:] 可以不输入

复制文件

把当前文件复制到远程服务器上:

scp /home/a.txt root@192.168.0.8:/home/

然后会提示输入192.168.0.8的root密码,接着就开始复制传输了。

复制文件夹

把test文件夹复制到远程服务器home路径下

scp -r /root/home2/test root@192.168.0.8:/home/
编辑
2023-07-06
凡猫购
00

该文章已加密,点击 阅读全文 并输入密码后方可查看。

编辑
2023-07-05
中间件
00

一、前言

因为CAS支持HTTP请求访问,而我们是快速入门上手,所以这期教程就不教大家如何配置HTTPS了,如果需要使用HTTPS,可以参考其他博客去云服务器申请证书或者使用JDK自行生成一个证书。

二、准备工作

下载CAS Server(直接下载压缩包就可以) 这里我们用的是5.3版本的服务端,5.3之前的服务端都是maven项目,大于5.3的服务端就是gradle项目

下载链接

image.png

三、CAS Server服务端搭建

1.解压我们前面下载好的压缩包。

2.然后进入解压好的目录,打开cmd窗口,输入命令:bulid.cmd run

image.png

看到BUILD SUCCESS就可以关掉这个cmd窗口了。

3.接下来用我们idea打开这个解压好的目录,拉取maven依赖

因为我们5.3版本是maven项目,因此先让他去下载依赖,耐心等待依赖加载完成~

4.拉取完依赖后,创建对应java目录和resources目录

image.png

5.在resources目录中,我们把target目录下的services文件夹和application.properties文件拷贝到我们的resources目录下

image.png

6.打开HTTPSandIMAPS-10000001这个文件,在里面加上http,让他支持http请求访问

image.png

7.然后打开我们application.properties文件,把这三行注释掉,这是给配置证书情况下使用的,也就是要给配置https使用的。我们这里暂时不用

image.png

8.接下来我们需要在我们的pom.xml文件中加上支持数据库链接的依赖 因为我们不需要用他的默认账号和密码,我们要用我自己数据库中的数据登录

image.png

注意这里MySQL驱动包的版本要和自己MySQL版本对应上

<dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-support-jdbc</artifactId> <version>${cas.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.21</version> </dependency>

9.然后我们还需要在application.properties文件中加上一些配置,具体说明看下图

image.png

#允许http cas.tgc.secure=false cas.serviceRegistry.initFromJson=true #查询账号密码sql,必须包含密码字段 cas.authn.jdbc.query[0].sql=select * from user where username = ? #指定上面的sql查询字段名(必须) cas.authn.jdbc.query[0].fieldPassword=password #指定过期字段,1为过期,若过期需要修改密码 cas.authn.jdbc.query[0].fieldExpired=expired #为不可用字段段,1为不可用, cas.authn.jdbc.query[0].fieldDisabled=disabled #数据库方言hibernate的知识 cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect #数据库驱动 cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver #数据库连接 cas.authn.jdbc.query[0].url=jdbc:mysql://localhost:3306/db_user2?useUnicode=true&characterEncoding=UTF-8 #数据库用户名 cas.authn.jdbc.query[0].user=root #数据库密码 cas.authn.jdbc.query[0].password= #默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密 cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULT cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8 cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5

注意下,这里我们配置文件中写了对密码进行MD5加密,因此我们数据库中存储的密码必须是MD5加密过的,不然的话登录不进去

image.png

可以这样去获取对应明文密码的MD5加密密码,然后存回数据库中的password字段中

image.png

select MD5('123456');

10.然后我们idea中配置Tomcat,就可以启动项目访问我们的服务端了

image.png

部署我们的cas服务端项目

image.png

image.png

image.png

这时候,我们就可以用我们数据库中的账号和密码进行登录了

image.png

image.png

四、CAS Client客户端搭建

1.先用我们的idea创建一个SpringBoot项目,然后在pom.xml文件中,引入对应的依赖,这边我直接贴上我客户端pom.xml里面的代码

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version><!--2.3.3.RELEASE--> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.cpc.cd</groupId> <artifactId>cas-client</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <!-- 整合web开发依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 引入mysql连接依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> <scope>runtime</scope> </dependency> <!--引入阿里巴巴druid连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.19</version> </dependency> <dependency> <groupId>net.unicon.cas</groupId> <artifactId>cas-client-autoconfig-support</artifactId> <version>2.3.0-GA</version> </dependency> </dependencies> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </pluginManagement> </build> </project>

2.然后我们要在这里加上对应配置文件代码,主要就是指明server端的地址以及当前客户端的地址

image.png

#本地cas server: port: 8090 #cas服务端配置 cas: server-url-prefix: http://localhost:8081/cas server-login-url: http://localhost:8081/cas/login #cas客户端配置 client-host-url: http://localhost:8090 #注意cas服务端的版本,如果较低这里需要使用CAS方式认证 validation-type: CAS3

3.然后在我们的启动类上要加上这个@EnableCasClient注解

image.png

4.接下来就是对CAS过滤器进行配置

首先是配置CASUtil.java文件,这个主要用来获取当前登录用户信息

package cpc.cd.util; import org.jasig.cas.client.authentication.AttributePrincipal; import org.jasig.cas.client.util.AbstractCasFilter; import org.jasig.cas.client.validation.Assertion; import javax.servlet.http.HttpServletRequest; /** * @author allentang * @create 2023-07-04 19:25 */ public class CASUtil { public static String getLoginNameFromCas(HttpServletRequest request) { Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION); if(assertion!= null) { AttributePrincipal principal = assertion.getPrincipal(); return principal.getName(); } else { return null; } } }

然后是LoginUserInfoFilter.java文件的配置,这里主要配置自定义的登录用户信息拦截器

package cpc.cd.filter; import com.alibaba.druid.util.StringUtils; import cpc.cd.util.CASUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * @author allentang * @create 2023-07-04 19:26 */ public class LoginUserInfoFilter implements Filter { Logger logger = LoggerFactory.getLogger(LoginUserInfoFilter.class); @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest request_ = (HttpServletRequest)request; String loginName = CASUtil.getLoginNameFromCas(request_); if(!StringUtils.isEmpty(loginName)){ logger.info("访问者 :" +loginName); request_.getSession().setAttribute("loginName", loginName); } chain.doFilter(request, response); } }

最后就是配置我们的CAS过滤器CASFilterConfig.java文件了

package cpc.cd.config; import cpc.cd.filter.LoginUserInfoFilter; import org.jasig.cas.client.authentication.AuthenticationFilter; import org.jasig.cas.client.session.SingleSignOutFilter; import org.jasig.cas.client.session.SingleSignOutHttpSessionListener; import org.jasig.cas.client.util.HttpServletRequestWrapperFilter; import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter; import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletListenerRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import javax.servlet.Filter; /** * @author allentang * @create 2023-07-04 19:27 */ @Configuration public class CASFilterConfig { @Value("${cas.server-url-prefix}") private String CAS_URL; @Value("${cas.client-host-url}") private String APP_URL; /** * 配置监听器 * @return */ @Bean public ServletListenerRegistrationBean servletListenerRegistrationBean(){ ServletListenerRegistrationBean listenerRegistrationBean = new ServletListenerRegistrationBean(); listenerRegistrationBean.setListener(new SingleSignOutHttpSessionListener()); listenerRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); return listenerRegistrationBean; } /** * 单点登录退出 * @return */ @Bean public FilterRegistrationBean singleSignOutFilter(){ FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new SingleSignOutFilter()); registrationBean.addUrlPatterns("/*"); registrationBean.addInitParameter("casServerUrlPrefix", CAS_URL ); registrationBean.setName("CAS Single Sign Out Filter"); registrationBean.setOrder(2); return registrationBean; } /** * 单点登录认证 * @return */ @Bean public FilterRegistrationBean AuthenticationFilter(){ FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new AuthenticationFilter()); registrationBean.addUrlPatterns("/*"); registrationBean.setName("CAS Filter"); registrationBean.addInitParameter("casServerLoginUrl",CAS_URL); registrationBean.addInitParameter("serverName", APP_URL ); registrationBean.setOrder(3); return registrationBean; } /** * 单点登录校验 * @return */ @Bean public FilterRegistrationBean cas30ProxyReceivingTicketValidationFilter(){ FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new Cas30ProxyReceivingTicketValidationFilter()); registrationBean.addUrlPatterns("/*"); registrationBean.setName("CAS Validation Filter"); registrationBean.addInitParameter("casServerUrlPrefix", CAS_URL ); registrationBean.addInitParameter("serverName", APP_URL ); registrationBean.setOrder(4); return registrationBean; } /** * 单点登录校验 * @return */ /* @Bean public FilterRegistrationBean Cas20ProxyReceivingTicketValidationFilter(){ FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new Cas20ProxyReceivingTicketValidationFilter()); registrationBean.addUrlPatterns("/*"); registrationBean.setName("CAS Validation Filter"); registrationBean.addInitParameter("casServerUrlPrefix", CAS_URL ); registrationBean.addInitParameter("serverName", APP_URL ); registrationBean.setOrder(4); return registrationBean; }*/ /** * 单点登录请求包装 * @return */ @Bean public FilterRegistrationBean httpServletRequestWrapperFilter(){ FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new HttpServletRequestWrapperFilter()); registrationBean.addUrlPatterns("/*"); registrationBean.setName("CAS HttpServletRequest Wrapper Filter"); registrationBean.setOrder(5); return registrationBean; } /** * 获取当前登录用户信息 * @return */ @Bean public FilterRegistrationBean getLoginUserInfoFilter(){ FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new LoginUserInfoFilter()); registrationBean.addUrlPatterns("/*"); registrationBean.setName("loginUserInfoFilter"); registrationBean.setOrder(6); return registrationBean; } }

5.然后就是写我们的测试文件,来测试下我们单点登录和登出了

package cpc.cd.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpSession; /** * @author allentang * @create 2023-07-03 13:47 */ @Controller public class TestController { @GetMapping("/hello") @ResponseBody public String hello() { return "word"; } //登出 @RequestMapping("/logout") public String logout(HttpSession session){ session.invalidate(); return "redirect:http://localhost:8081/cas/logout"; } }

运行我们客户端的SpringBoot的项目,当我们输入 http://localhost:8090/hello 时候,它会自动跳到我们服务端的登录界面

当我们输入完账号密码登录后,再输入 http://localhost:8090/hello 时候就能正常访问内容了

当我们输入 http://localhost:8090/logout 时候,可以发现能正常退出,再输入http://localhost:8090/hello 时候需要重新登录了

编辑
2023-07-05
其他
00
编辑
2023-06-28
JAVA
00

快试试用 API Key 来保护你的 SpringBoot 接口安全吧

5d677760bd80c2fe10d93765088b59ed.jpg

1、概述

安全性在REST API开发中扮演着重要的角色。一个不安全的REST API可以直接访问到后台系统中的敏感数据。因此,企业组织需要关注API安全性。

Spring Security 提供了各种机制来保护我们的 REST API。其中之一是 API 密钥。API 密钥是客户端在调用 API 调用时提供的令牌。

在本教程中,我们将讨论如何在Spring Security中实现基于API密钥的身份验证。

2、REST API Security

Spring Security可以用来保护REST API的安全性。REST API是无状态的,因此不应该使用会话或cookie。相反,应该使用Basic authentication,API Keys,JWT或OAuth2-based tokens来确保其安全性。

2.1. Basic Authentication

Basic authentication是一种简单的认证方案。客户端发送HTTP请求,其中包含Authorization标头的值为Basic base64_url编码的用户名:密码。Basic authentication仅在HTTPS / SSL等其他安全机制下才被认为是安全的。

2.2. OAuth2

OAuth2是REST API安全的行业标准。它是一种开放的认证和授权标准,允许资源所有者通过访问令牌将授权委托给客户端,以获得对私有数据的访问权限。

2.3. API Keys

一些REST API使用API密钥进行身份验证。API密钥是一个标记,用于向API客户端标识API,而无需引用实际用户。标记可以作为查询字符串或在请求头中发送。

3、用API Keys保护REST API

3.1 添加Maven 依赖

让我们首先在我们的pom.xml中声明spring-boot-starter-security依赖关系:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>

3.2 创建自定义过滤器(Filter)

实现思路是从请求头中获取API Key,然后使用我们的配置检查秘钥。在这种情况下,我们需要在Spring Security 配置类中添加一个自定义的Filter。

我们将从实现GenericFilterBean开始。GenericFilterBean是一个基于javax.servlet.Filter接口的简单Spring实现。

让我们创建AuthenticationFilter类:

public class AuthenticationFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { try { Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest) request); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (Exception exp) { HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE); PrintWriter writer = httpResponse.getWriter(); writer.print(exp.getMessage()); writer.flush(); writer.close(); } filterChain.doFilter(request, response); } }

我们只需要实现doFilter()方法,在这个方法中我们从请求头中获取API Key,并将生成的Authentication对象设置到当前的SecurityContext实例中。

然后请求被传递给其余的过滤器处理,接着转发给DispatcherServlet最后到达我们的控制器。

在AuthenticationService类中,实现从Header中获取API Key并构造Authentication对象,代码如下:

public class AuthenticationService { private static final String AUTH_TOKEN_HEADER_NAME = "X-API-KEY"; private static final String AUTH_TOKEN = "Baeldung"; public static Authentication getAuthentication(HttpServletRequest request) { String apiKey = request.getHeader(AUTH_TOKEN_HEADER_NAME); if ((apiKey == null) || !apiKey.equals(AUTH_TOKEN)) { throw new BadCredentialsException("Invalid API Key"); } return new ApiKeyAuthentication(apiKey, AuthorityUtils.NO_AUTHORITIES); } }

在这里,我们检查请求头是否包含 API Key,如果为空 或者Key值不等于密钥,那么就抛出一个 BadCredentialsException。如果请求头包含 API Key,并且验证通过,则将密钥添加到安全上下文中,然后调用下一个安全过滤器。getAuthentication 方法非常简单,我们只是比较 API Key 头部和密钥是否相等。

为了构建 Authentication 对象,我们必须使用 Spring Security 为了标准身份验证而构建对象时使用的相同方法。所以,需要扩展 AbstractAuthenticationToken 类并手动触发身份验证。

3.3. 扩展AbstractAuthenticationToken

为了成功地实现我们应用的身份验证功能,我们需要将传入的API Key转换为AbstractAuthenticationToken类型的身份验证对象。AbstractAuthenticationToken类实现了Authentication接口,表示一个认证请求的主体和认证信息。

让我们创建ApiKeyAuthentication类:

public class ApiKeyAuthentication extends AbstractAuthenticationToken { private final String apiKey; public ApiKeyAuthentication(String apiKey, Collection<?extends GrantedAuthority> authorities) { super(authorities); this.apiKey = apiKey; setAuthenticated(true); } @Override public Object getCredentials() { return null; } @Override public Object getPrincipal() { return apiKey; } }

ApiKeyAuthentication 类是类型为 AbstractAuthenticationToken 的对象,其中包含从 HTTP 请求中获取的 apiKey 信息。在构造方法中使用 setAuthenticated(true) 方法。因此,Authentication对象包含 apiKey 和authenticated字段:

977ca77dc6cc6ededff8f1510962f434.png

3.4. Security Config

通过创建建一个SecurityFilterChain bean,可以通过编程方式把我们上面编写的自定义过滤器(Filter)进行注册。

我们需要在 HttpSecurity 实例上使用 addFilterBefore() 方法在 UsernamePasswordAuthenticationFilter 类之前添加 AuthenticationFilter。

创建SecurityConfig 类:

@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf() .disable() .authorizeRequests() .antMatchers("/**") .authenticated() .and() .httpBasic() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .addFilterBefore(new AuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } }

此外注意代码中我们吧绘画策略(session policy)设置为无状态(STATELESS),因为我们使用的是REST。

3.5. ResourceController

最后,我们创建ResourceController,实现一个Get请求 /home

@RestController public class ResourceController { @GetMapping("/home") public String homeEndpoint() { return "Baeldung !"; } } 3.6. 禁用 Auto-Configuration @SpringBootApplication(exclude = {SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class}) public class ApiKeySecretAuthApplication { public static void main(String[] args) { SpringApplication.run(ApiKeySecretAuthApplication.class, args); } }

4. 测试

我们先不提供API Key进行测试

curl --location --request GET 'http://localhost:8080/home'

返回 401 未经授权错误。

请求头中加上API Key后,再次请求

curl --location --request GET 'http://localhost:8080/home' \ --header 'X-API-KEY: Baeldung'

请求返回状态200