1.Spring Security介绍
几乎每个信息系统都需要认证授权保障其可靠性和安全性,当我们用spring框架开发时,Spring Security是一个不错的选择。
Spring Security是一个专注于为Java应用程序提供身份认证和授权功能的强大框架。它基于Spring框架,并提供了全面的安全服务集成,包括认证(Authentication)、授权(Authorization)、攻击防护和会话管理等功能。
2.集成Spring Security
2.1部署认证服务工程
在项目中新建一个springboot工程,并创建controller类定义接口
@Slf4j
@RestController
@RequestMapping("/auth")
public class LoginController {
@RequestMapping("/login-success")
public String loginSuccess() {
return "登录成功";
}
@RequestMapping("/r/r1")
public String r1() {
return "访问r1资源";
}
@RequestMapping("/r/r2")
public String r2() {
return "访问r2资源";
}
}
启动服务,此时我们可以 在浏览器中访问http://localhost:63070/auth/login-success和http://localhost:63070/auth/r/r1,结果如下
2.2导入Spring Security依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
此时再次访问http://localhost:63070/auth/r/r1时,会自动进入/login登录页面,要先登录才能访问/r/r1资源。
/login是spring security提供的,加载可能较慢。
默认用户名user,每次启动服务时密码会自动重新生成并在控制台输出
3自定义用户认证授权测试
1.自定义配置类继承WebSecurityConfigurerAdapter配置类
2.通过重写 configure(HttpSecurity http)
方法来定义安全规则和访问控制。
注意:NoOpPasswordEncoder
通常用于测试或者开发环境中,因为它并不会对密码进行任何加密,而是将密码以明文的形式存储和验证。存在严重的安全风险,因为密码可以很容易地被窃取和滥用。建议不要在生产环境中使用类似的明文密码存储方式。
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public UserDetailsService userDetailsService(){
//这里配置用户信息,这里暂时使用这种方式将用户存储在内存中
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("xiyangyang").password("123").authorities("p1").build());
manager.createUser(User.withUsername("meiyangyang").password("456").authorities("p2").build());
return manager;
}
//获取一个密码编码器对象
@Bean
public PasswordEncoder passwordEncoder(){
//密码为明文方式
return NoOpPasswordEncoder.getInstance();
}
//配置安全拦截机制
@Override
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()
.antMatchers("/r/**").authenticated()//访问/r开始的请求需要认证通过
.anyRequest().permitAll()//其它请求全部放行
.and()
.formLogin().successForwardUrl("/login-success");//登录成功跳转到/login-success
http.logout().logoutUrl("/logout");//退出地址
}
}
此时便可以用自定义的用户名和密码登录访问服务
4.集成OAuth2
4.1OAuth2介绍
4.1.2什么是OAuth2
OAuth 2(Open Authorization 2)是一种授权框架,允许第三方应用程序通过使用授权服务器进行限制的方式,访问受保护的资源,而无需用户将其凭据直接提供给第三方应用程序。它在网络服务之间提供了安全的授权标准。
4.1.3为什么使用OAuth2
OAuth 2.0 是一个开放的标准,被广泛接受和支持。许多大型的互联网服务提供商和平台都实现了 OAuth 2.0,使得开发者可以使用统一的协议和工具来实现授权流程,而无需为每个服务提供商单独开发授权机制。我们熟知的微信扫码就是基于这种OAuth2协议实现的
4.1.4OAuth2的授权模式
Spring Security支持OAuth2认证,OAuth2提供授权码模式、密码模式、简化模式、客户端模式等授权模式,微信扫码登录就是基于授权码模式,其中授权码模式和密码模式应用较多,下面使用Spring Security演示授权码模式、密码模式
4.2导入OAuth2.0依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
4.3创建授权服务器配置类
4.3.1获取并配置 AuthenticationManager
的实例
在之前创建的WebSecurityConfig 中注入AuthenticationManager
,以支持身份验证功能
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
4.3.1创建授权服务器配置类AuthorizationServer
@EnableAuthorizationServer 注解标识并继承AuthorizationServerConfigurerAdapter来配置OAuth2.0 授权服务器
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Resource(name="authorizationServerTokenServicesCustom")
private AuthorizationServerTokenServices authorizationServerTokenServices;
@Autowired
private AuthenticationManager authenticationManager;
//客户端详情服务
@Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory()// 使用in-memory存储
.withClient("baidu")// client_id
.secret("baidu")//客户端密钥
// .secret(new BCryptPasswordEncoder().encode("XcWebApp"))//客户端密钥
.resourceIds("baidu-resource")//资源列表
// 该client允许的授权类型authorization_code,password,refresh_token,implicit,client_credentials
.authorizedGrantTypes("authorization_code", "password","client_credentials","implicit","refresh_token")
.scopes("all")// 允许的授权范围
.autoApprove(false)//false跳转到授权页面
//客户端接收授权码的重定向地址
.redirectUris("http://www.baidu.com")
;
}
//令牌端点的访问配置
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(authenticationManager)//认证管理器
.tokenServices(authorizationServerTokenServices)//令牌管理服务
.allowedTokenEndpointRequestMethods(HttpMethod.POST);
}
//令牌端点的安全配置
@Override
public void configure(AuthorizationServerSecurityConfigurer security){
security
.tokenKeyAccess("permitAll()") //oauth/token_key是公开
.checkTokenAccess("permitAll()") //oauth/check_token公开
.allowFormAuthenticationForClients() //表单认证(申请令牌)
;
}
}
4.2新建令牌配置类
@Configuration
public class TokenConfig {
@Autowired
TokenStore tokenStore;
@Bean
public TokenStore tokenStore() {
//使用内存存储令牌(普通令牌)
return new InMemoryTokenStore();
}
//令牌管理服务
@Bean(name="authorizationServerTokenServicesCustom")
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service=new DefaultTokenServices();
service.setSupportRefreshToken(true);//支持刷新令牌
service.setTokenStore(tokenStore);//令牌存储策略
service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
return service;
}
}
4.4授权码模式测试
启动服务,登录成功后在浏览器输入以下地址:
注意:我们在AuthorizationServer已经配置好了以下参数
解释:
client_id:客户端的唯一标识符
response_type:授权码模式固定为code。
scope:客户端权限。
redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数(授权码)
此时会出现以下界面选择approve,点击authorize则会跳转到百度页面
我们可以看到地址为https://www.baidu.com/?code=Rd8kGZ,此时便可以拿到授权码Rd8kGZ
注意:授权码只能使用一次。
拿到授权码后,使用postman或者httpclient执行
注意更改code为=Rd8kGZ
POST localhost:63070/auth/oauth/token?client_id=baidu&client_secret=baidu&grant_type=authorization_code&code=Rd8kGZ&redirect_uri=http://www.baidu.com
此时申请令牌成功
解释:
access_token:这是一个访问令牌,用于客户端在访问受保护的资源时进行身份验证和授权。
token_type:指定了访问令牌的类型,这里是 bearer。是OAuth 2.0中定义的一种令牌类型,表示客户端可以使用令牌来访问被授权的资源。
refresh_token:刷新令牌,用于获取新的访问令牌。刷新令牌有更长的有效期,用于在访问令牌过期时获取新的访问令牌。
expires_in:
表示访问令牌的有效期限(秒)。
scope:指定了授权的范围,all 表示这个访问令牌被授予了访问所有资源的权限。
4.5密码模式测试
密码模式相对授权码模式简单,不需要借助浏览器。
可以直接postman或者httpclient执行
POST localhost:63070/auth/oauth/token?client_id=baidu&client_secret=baidu&grant_type=password&username=xiyangyang&password=123
解释:
client_id=baidu: 这是客户端的唯一标识符。
client_secret=baidu: 客户端的密钥,用于对客户端的身份进行验证。
grant_type=password: 指定了密码授权模式。
username=xiyangyang: 用户名
password=123: 用户密码
执行输出:
注意:密码模式十分简单,但却意味着将用户敏感信息泄露给了client,建议仅在确保安全的环境下使用。