此文之前,项目已经添加了数据库DAO服务接口、资源访问目录、以及数据访问的html页面,同时项目集成了spring security,并替换了登录授权页面;但是,系统用户存储代码之中,而且只注册了admin和user两个用户。在实际上线项目中,用户的数量和全限时需要不定时更改的,登录的账户也需要进行角色和失效的确认。
为了更好的维护用户数据,更好的保护系统资源,我们引入Oauth2.0。OAuth是一个开放标准,也就是一个授权框架,使应用程序能够访问项目外的资源,允许用户在第三方应用访问存储在其他服务器上的私密资源,而在整个过程不需要提供用户名和密码给到第三方应用,可以通过提供一个令牌(token)实现该功能,采用令牌的方式可以让用户灵活的对第三方应用授权或收回权限。
1、修改醒目pom.xml文件,引入Oauth2.0 jar包,添加如下依赖:
<!--oauth2-->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.2.1.Release</version>
</dependency>
<!--页面要用到的框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
添加完成之后,右键点击项目名称,弹出菜单中选择“maven”→“update project”,更新系统jar包。
2、开启@EnableAuthorizationServer注解
开启@EnableAuthorizationServer注解,该注解会自动添加OAuth2的多个endpoint。
- /oauth/authorize:验证接口, AuthorizationEndpoint
- /oauth/token:获取token
- /oauth/confirm_access:用户授权
- /oauth/error:认证失败
- /oauth/check_token:资源服务器用来校验token
- /oauth/token_key:jwt模式下获取公钥;位于:TokenKeyEndpoint ,通过 JwtAccessTokenConverter 访问key
继承AuthorizationServerConfigurerAdapter,配置OAuth2认证服务器,需要配置授权和Token相关的三个配置。
- AuthorizationServerSecurityConfigurer:声明安全约束,允许那些请求可以访问和禁止访问。
- ClientDetailsServiceConfigurer:配置独立的client客户端信息,包括授权范围、授权方式、客户端权限等。授权方式包括password、implicit、client_redentials、authorization_code四种方式,其中密码授权方式必须结合 AuthenticationManager 进行配置。
- AuthorizationServerEndpointsConfigurer:配置授权服务器的Token存储方式、Token配置、授权模式
Token存储配置,默认使用DefaultTokenServices管理生命周期。Token存储通过配置 TokenStore,默认使用内存存储,包括一下存储方式。
- InMemoryTokenStore 默认方式,保存在本地内存
- JdbcTokenStore 存储数据库
- RedisTokenStore 存储Redis,这应该是微服务下比较常用方式
- JwtTokenStore 分布式跨域JWT存储方式
此项目采用JdbcTokenStore方式进行存储,配置文件如下:
package com.SJL.Spring.Oauth.Config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
@Configuration
@EnableAuthorizationServer
public class OAuthAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private OAuthWebSecurityConfig oauthWebSecurityConfig;
@Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
//客户端token调用许可
.tokenKeyAccess("permitAll()")
//客户端校验token访问许可
.checkTokenAccess("permitAll()")
.allowFormAuthenticationForClients();
}
//refresh_token 单独配置UserDetailsService
@Bean
public UserDetailsService userDetailsServiceRefresh_token() {
return oauthWebSecurityConfig.userDetailsService();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(new JdbcTokenStore(dataSource)).userDetailsService(userDetailsServiceRefresh_token())// 设置令牌
.authenticationManager(authenticationManager);
}
@Bean // 声明 ClientDetails实现
public ClientDetailsService clientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService());
}
}
备注:文件拷贝过程如下几个函数需要自行定义,自动生成代码中未生成代码,详细如下:
(1)sysUserMapper.findByName(username);
(2)sysUserMapper.updateUNLockedByUserId(username);
(3)sysUserMapper.updatelockTimeByUserId(username, 0L);
(4)sysUserMapper.updatenLockByUserId(username, 0L);
(5)sysUserService.findPermissions(username)
3、开启@EnableResourceServer注解,在服务中声明资源服务器。
打开ActionApp.java文件,添加@EnableResourceServer
(1)删除WebSecurityConfig.java文件,拷贝OAuthWebSecurityConfig.java文件
(2)配置App客户端ID和密码
打开“application.yml”文件,添加如下代码:
spring:
security:
oauth2:
client:
clientId: zrBm49l73k996cJj9471
clientSecret: b400wT1D62
accessTokenUri: ${auth-server}/oauth/token
userAuthorizationUri: ${auth-server}/oauth/authorize
scope: all
resource:
userInfoUri: ${auth-server}/user
备注:clientIdy和clientSecret为app唯一标识码,注册在库表“oauth_client_details”中
4、获取服务令牌
上述配置采用“授权码”模式,获取令牌首先获取授权码,再用授权码换取令牌,用户使用令牌访问系统资源;
(1)获取授权码
在浏览器中输入测试地址http://localhost:2885/oauth/authorize?client_id=zrBm49l73k996cJj9471&response_type=code&redirect_uri=http://localhost:2885/assets/img
浏览器返回http://localhost:2885/assets/img?code= 56Lv8n
(2)换取令牌
利用postman的post方式,发送http://localhost:2885/oauth/token?grant_type=authorization_code&code=56Lv8n&client_id=zrBm49l73k996cJj9471&client_secret=b400wT1D62&redirect_uri=http://localhost:2885/assets/img,获取令牌,postman返回如下所示:
其中, "access_token": "c334c2ab-aaf7-49c6-8ce5-f23459a6274f",就是我们获得令牌
(3)利用令牌访问资源
浏览器中,输入http://localhost:2885/assets/img/%E6%B5%B7%E8%B1%9A.png?access_token=c334c2ab-aaf7-49c6-8ce5-f23459a6274f,即可访问资源,如下图所示
到此,springboot+spring security+Oauth2.0的授权服务器和资源服务器的搭建工作就结束了。下文讲详细讲解Oauth2.0的几个重要结构文件,详细配置,以及授权错误处理,实现系统登陆的闭环测试。