扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
本篇内容介绍了“Spring Security OAuth2怎么获取token”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
成都创新互联公司主营铁锋网站建设的网络公司,主营网站建设方案,app软件开发,铁锋h5成都小程序开发搭建,铁锋网站营销推广欢迎铁锋等地区企业咨询
什么是OAuth3
OAuth 2.0 的标准是 RFC 6749 文件 OAuth 引入了一个授权层,用来分离两种不同的角色:客户端和资源所有者。......资源所有者同意以后,资源服务器可以向客户端颁发令牌。客户端通过令牌,去请求数据:
OAuth3核心-授权
OAuth3的四种方式
模式 |
---|
授权码(authorization-code) |
隐藏式(implicit) |
密码式(password) |
客户端凭证(client credentials) |
4. 握手流程(摘自RFC6749)
5. SpringSecurity
基于Spring的企业应用系统提供声明式的安全訪问控制解决方式的安全框架(简单说是对访问权限进行控制嘛),应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分
*6. SpringSecurity 中默认的内置过滤列表:
(其他后续补充)
1. 环境
SpringBoot 2.1.0.RELEASE jdk1.8 SpringCloud Greenwich.SR1 consul服务发现与注册
项目
项目 | 描述 |
---|---|
fp-commons | jar公用 |
fp-gateway | 网关 |
fp-authorization-server | OAuth3认证服务器 |
3. 项目搭建 |
(1)网关搭建 frame-gateway pom.xml: 引入依赖文件;在启动器上添加 注册监听@EnableDiscoveryClient 并注入 DiscoveryClientRouteDefinitionLocator application.xml: spring-main-allow-bean-definition-overriding: true (相同名称注入允许覆盖) spring.application.name:SpringCloud-consul-gateway (设置应用名称必须) bootstrap.yml 中配置 consul(比application先加载) prefer-ip-address: true (使用ip注册,有些网络会出现用主机名来获取注册的问题)
(2)fp-commons jar公用
pom.xml: 引入依赖文件;添加一些公用方法
(3)fp-authorization-server
(git中提交了一个至简版本的OAuth3认证中心,只有token生成功能,没有资源保护以及认证)
只需要配置AuthorizationServerConfigurerAdapter 认证服务器 以及WebSecurityConfigurerAdapter SpringSecurity配置 两个文件,就能实现token生成。 如果没有需要保护的资源不用ResourceServerConfigurerAdapter 资源服务器配置
基本的SpringBoot搭建略过,这里只介绍SpringSecurity OAuth3的token实现,基本的数据处理以及其他看源码。
首先完成ClientDetailsService的自定义实现(获取客户端相关信息)
/** * @Description 自定义客户端数据 * @Author wwz * @Date 2019/07/28 * @Param * @Return */ @Service public class MyClientDetailsService implements ClientDetailsService { @Autowired private AuthClientDetailsMapper authClientDetailsMapper; @Override public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException { AuthClientDetails clientDetails = authClientDetailsMapper.selectClientDetailsByClientId(clientId); if (clientDetails == null) { throw new ClientRegistrationException("该客户端不存在"); } MyClientDetails details = new MyClientDetails(clientDetails); return details; } }
以及UserDetailsService的自定义实现(获取用户相关信息)
/** * @Description 自定义用户验证数据 * @Author wwz * @Date 2019/07/28 * @Param * @Return */ @Service public class MyUserDetailsService implements UserDetailsService { @Autowired private AuthUserMapper authUserMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 自定义用户权限数据 AuthUser authUser = authUserMapper.selectByUsername(username); if (authUser == null) { throw new UsernameNotFoundException("用户名不存在"); } if (!authUser.getValid()) { throw new UsernameNotFoundException("用户不可用"); } SetgrantedAuthorities = new HashSet<>(); if (authUser.getAuthRoles() != null) { for (AuthRole role : authUser.getAuthRoles()) { // 当前角色可用 if (role.getValid()) { //角色必须是ROLE_开头 GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getRoleName()); grantedAuthorities.add(grantedAuthority); if (role.getAuthPermissions() != null) { for (AuthPermission permission : role.getAuthPermissions()) { // 当前权限可用 if (permission.getValid()) { // 拥有权限设置为 auth/member/GET 可以访问auth服务下面 member的查询方法 GrantedAuthority authority = new SimpleGrantedAuthority(permission.getServicePrefix() + "/" + permission.getUri() + "/" + permission.getMethod()); grantedAuthorities.add(authority); } } } } //获取权限 } } MyUserDetails userDetails = new MyUserDetails(authUser, grantedAuthorities); return userDetails; } }
然后新建认证服务配置MySecurityOAuth3Config,继承AuthorizationServerConfigurerAdapter
/** * @Description OAuth3认证服务配置 * @Author wwz * @Date 2019/07/28 * @Param * @Return */ @Configuration @EnableAuthorizationServer public class MySecurityOAuth3Config extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; // 认证方法入口 @Autowired private redisConnectionFactory connectionFactory; // redis连接工厂 @Autowired private MyUserDetailsService userDetailsService; // 自定义用户验证数据 @Autowired private MyClientDetailsService clientDetailsService; // 自定义客户端数据 // 加密方式 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } /*** * 设置token用redis保存 */ @Bean public TokenStore tokenStore() { //token保存在redis中(也可以保存在数据库、内存中new InMemoryTokenStore()、或者jwt;)。 //如果保存在中间件(数据库、Redis),那么资源服务器与认证服务器可以不在同一个工程中。 //注意:如果不保存access_token,则没法通过access_token取得用户信息 RedisTokenStore redis = new RedisTokenStore(connectionFactory); return redis; } /** * 配置令牌端点(Token Endpoint)的安全约束 */ @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer .tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()") .allowFormAuthenticationForClients(); // 允许表单登录 } /** * 配置 oauth_client_details【client_id和client_secret等】信息的认证【检查ClientDetails的合法性】服务 * 设置 认证信息的来源:数据库,内存,也可以自己实现ClientDetailsService的loadClientByClientId 方法自定义数据源 * 自动注入:ClientDetailsService的实现类 JdbcClientDetailsService (检查 ClientDetails 对象) * 这个方法主要是用于校验注册的第三方客户端的信息,可以存储在数据库中,默认方式是存储在内存中 */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // 设置从自定义接口获取客户端信息 clients.withClientDetails(clientDetailsService); // clients.inMemory() // 使用in-memory存储 // .withClient("client_name") // client_id // .secret(passwordEncoder().encode("111")) // client_secret // .redirectUris("http://localhost:8001") // // 该client允许的授权类型 // .authorizedGrantTypes("password", "authorization_code", "refresh_token", "client_credentials") // .scopes("app", "app1", "app3"); // 允许的授权范围 } /** * 配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services) */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .tokenStore(tokenStore()) // 配置token存储 .userDetailsService(userDetailsService) // 配置自定义的用户权限数据,不配置会导致token无法刷新 .authenticationManager(authenticationManager) .tokenServices(defaultTokenServices()); // 加载token配置 } /** * 把认证的token保存到redis *注意,自定义TokenServices的时候,需要设置@Primary,否则报错,
* 自定义的token * 认证的token是存到redis里的 若ClientDetails中设定了有效时间则以设定值为准 */ @Primary @Bean public DefaultTokenServices defaultTokenServices() { DefaultTokenServices tokenServices = new DefaultTokenServices(); tokenServices.setTokenStore(tokenStore()); tokenServices.setSupportRefreshToken(true); tokenServices.setClientDetailsService(clientDetailsService); tokenServices.setAccessTokenValiditySeconds(60 * 60 * 12); // token有效期自定义设置,默认12小时 tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7); // refresh_token默认30天 return tokenServices; } }
接着新建Security的配置MySecurityConfig 继承WebSecurityConfigurerAdapter
** * @Description security 配置 * ResourceServerConfigurerAdapter 是比WebSecurityConfigurerAdapter 的优先级低的 * @Author wwz * @Date 2019/07/28 * @Param * @Return */ @Configuration @EnableWebSecurity @Order(2) // WebSecurityConfigurerAdapter 默认为100 这里配置为2设置比资源认证器高 public class MySecurityConfig extends WebSecurityConfigurerAdapter { // 自定义用户验证数据 @Bean public MyUserDetailsService userDetailsService() { return new MyUserDetailsService(); } // 加密方式 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } // 验证器加载 @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity http) throws Exception { http // 匹配oauth相关,匹配健康,匹配默认登录登出 在httpSecurity处理,,其他到ResourceServerConfigurerAdapter OAuth3处理 1 .requestMatchers().antMatchers("/oauth/**", "/actuator/health", "/login", "/logout") .and() // 匹配的全部无条件通过 permitAll 2 .authorizeRequests().antMatchers("/oauth/**", "/actuator/health", "/login", "/logout").permitAll() // 匹配条件1的 并且不再条件2通过范围内的其他url全部需要验证登录 .and().authorizeRequests().anyRequest().authenticated() // 启用登录验证 .and().formLogin().permitAll(); // 不启用 跨站请求伪造 默认为启用, 需要启用的话得在form表单中生成一个_csrf http.csrf().disable(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder()); } }
到此,SpringSecurity OAuth3至简的项目搭建完成,下一步为4种token的基本验证
password模式认证
请求地址:
http://192.168.3.14:8001/auth/oauth/token?grant_type=password&username=username&password=111
参数:
参数名 | 说明 |
---|---|
grant_type | 类型必须为password |
username | 用户名 |
password | 密码 |
client_id | 客户端id |
client_serce | 客户端密码 |
请求: |
响应: 返回信息 包括 access_token 通行token, refresh_token 重新申请token,scope资源范围
验证:
无权访问页面
访问页面
client_credentials模式
请求地址:
http://192.168.3.14:8001/auth/oauth/token?grant_type=client_credentials&client_id=client_name&client_secret=111
参数:
参数名 | 说明 |
---|---|
grant_type | 类型必须为client_credentials |
client_id | 客户端id |
client_serce | 客户端密码 |
请求 |
响应:返回参数 包括access_token,scope 权限范围 (但是不包含refresh_token)
验证:默认无任何权限 当然,这里的权限也可以在ClientDetails中的authorities字段中默认增加权限
refresh_token 模式
refresh_token 只有请求过一次并且获取到refresh_token才能用该值重新申请可用token,并且,申请了新的token后,原先为过期的token立即失效,refresh_token作为新的token的refresh_token继续使用。
请求地址:
http://192.168.3.14:8001/auth/oauth/token?grant_type=refresh_token&refresh_token=384b02e7-66d5-4d5b-8179-53793f7ba40b
参数
参数名 | 说明 |
---|---|
grant_type | 类型必须为refresh_token |
refresh_token | 刷新token |
请求:
响应:返回结果包括 新的token,原refresh_token,scope权限
authorization_code模式
请求:
第一次请求:http://192.168.3.14:8001/auth/oauth/authorize?response_type=code&client_id=client_name&redirect_uri=http://192.168.3.14:8001/auth/oauth/token&scope=auth 第二次请求:http://192.168.3.14:8001/auth/oauth/token?grant_type=authorization_code&redirect_uri=http://192.168.3.14:8001/auth/oauth/token&code=mbeBvY
参数:
第一次参数
参数名 | 说明 |
---|---|
response_type | 类型必须为code |
client_id | 客户端id |
redirect_uri | 返回code地址 与后端client_Id对应 redirect_uri,与第二步请求token对应 |
第二次参数
参数名 | 说明 |
---|---|
grant_type | 类型必须为authorization_code |
code | 为返回code 这里为 mbeBvY |
redirect_uri | 返回code地址 上一步redirect_uri http://192.168.3.14:8001/auth/oauth/token |
上述,填错一个就会报错code只能使用一次 再次请求失效
第一次请求后跳转 登录界面
登录后跳转授权页面然后返回到携带的redirect_uri路径 并带上code
第二次请求
响应:返回结果包括 token, refresh_token,scope权限
“Spring Security OAuth2怎么获取token”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注创新互联网站,小编将为大家输出更多高质量的实用文章!
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流