Spring Security + OAuth2.0
授权服务器
授权服务器中有4个端点。说明如下:
- Authorize Endpoint :授权端点,进行授权。
- Token Endpoint :令牌端点,经过授权拿到对应的Token。
- lntrospection Endpoint :校验端点,校验Token的合法性。
- Revocation Endpoint :撤销端点,撤销授权。
Spring Security Oauth2架构
说明如下:
- 用户访问,此时没有Token。Oauth2RestTemplate会报错,这个报错信息会被Oauth2ClientContextFilter捕获并重定向到认证服务器。
- 认证服务器通过Authorization Endpoint进行授权,并通过AuthorizationServerTokenServices生成授权码并返回给客户端。
- 客户端拿到授权码去认证服务器通过Token Endpoint调用AuthorizationServerTokenServices生成Token并返回给客户端。
- 客户端拿到Token去资源服务器访问资源,一般会通过Oauth2AuthenticationManager调用ResourceServerTokenServices进行校验。校验通过可以获取资源。
Spring Security Oauth2授权码模式
环境搭建
(2)pom依赖
<!-- spring cloud中的oauth2依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><!-- spring cloud中的security依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-security</artifactId></dependency><!-- web模块 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
(3)pojo
在pojo包下自定一个实体类User,但是此类必实现UserDetails接口。如下:
package com.sec.kun.pojo;import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;
import java.util.List;/*** @Author zhoukun* @Date 2021/2/17 15:45*/
/** 自定义User类,需实现UserDetails接口*/
public class User implements UserDetails {private String username;private String password;private List<GrantedAuthority> authorities;// 构造方法public User(String username, String password, List<GrantedAuthority> authorities) {this.username = username;this.password = password;this.authorities = authorities;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return authorities;}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}
(4)Spring Security配置类
config包下创建SecurityConfig配置类,如下:
package com.sec.kun.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;/*** @Author zhoukun* @Date 2021/2/17 15:55*/
/** Spring Security配置类*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/oauth/**","/login/**","/logout/**").permitAll()//放行.anyRequest().authenticated()//其他路径拦截.and().formLogin().permitAll()//表单提交放行.and().csrf().disable();//csrf关闭}// 注册PasswordEncoder@Beanpublic PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder();}
}
(5)自定义登录逻辑
service包下创建UserDetailsServiceImpl类,如下:
package com.sec.kun.service.Impl;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import com.sec.kun.pojo.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;/*** @Author zhoukun* @Date 2021/2/17 15:56*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//实际是根据用户名去数据库查,这里就直接用静态数据了if(!username.equals("86547462")) {throw new UsernameNotFoundException("用户名不存在!");}// 密码加密String password = passwordEncoder.encode("123456");//创建User用户,自定义的UserUser user = new User(username,password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));return user;}
}
(6)认证服务配置
在config包下创建认证服务的配置类AuthorizationServerConfig,如下:
package com.sec.kun.config;/*** @Author zhoukun* @Date 2021/2/17 15:59*/import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.password.PasswordEncoder;
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;/*** 授权服务器*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory()//内存中.withClient("client")//客户端ID.secret(passwordEncoder.encode("zk2000208"))//秘钥.redirectUris("https://www.bilibili.com")//重定向到的地址.scopes("all")//授权范围.authorizedGrantTypes("authorization_code");//授权类型为授权码模式}
}
(7)资源服务配置
在config包下创建资源服务的配置类
/** 资源服务配置*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter{@Overridepublic void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated().and().requestMatchers().antMatchers("/user/**");}}
(8)controller
在controller包下创建UserController类,如下:
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/getCurrentUser")public Object getCurrentUser(Authentication authentication) {return authentication.getPrincipal();}}
测试
(1)获取授权码
启动项目,访问:http://localhost:8000/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=all
说明:
http://localhost:8000:这是项目端口。
/oauth/authorize?response_type=code:获取授权码的固定写法。
client_id:这是客户端ID,就是在授权服务中定义的:
重定向到了百度首页,并且拿到了授权码。
(2)获取令牌
因为要发送post请求,所以使用postman。
url:http://localhost:8000/oauth/token
左边的type选择Basic Auth,右边的用户名为客户端ID,密码是定义好的。
发送请求,返回如下:
(3)获取资源服务器资源
需要携带通行令牌来获取。还是post请求:http://localhost:8000/user/getCurrentUser
Spring Security Oauth2密码模式
环境搭建
直接在授权码模式的基础上进行修改了。
(1)修改SecurityConfig
直接在里面加:
//注册AuthenticationManager@Beanpublic AuthenticationManager getAuthenticationManager() throws Exception {return super.authenticationManager();}
(2)修改AuthorizationServerConfig
直接在里面加:
/*** 密码模式*/@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate UserDetailsServiceImpl userDetailsServiceImpl;//密码模式需要配置@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints)throws Exception {endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsServiceImpl);}
然后下面添加密码模式:
测试
(1)获取Token令牌
启动项目,直接在postman中发送:http://localhost:8000/oauth/token
(2)获取资源
现在直接可以携带令牌去访问资源:http://localhost:8000/user/getCurrentUser
Redis中存储Token令牌
将token直接存在内存中,这在生产环境中是不合理的,下面将其改造成存储在Redis中。
(1)pom依赖
在pom中添加如下依赖:
<!-- redis依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
(2)yml配置
在application.yml中添加:
spring:redis:host: localhostport: 6379password: 123456
在AuthorizationServerConfig加入
/*** redis工厂,默认使用lettue*/@Autowiredpublic RedisConnectionFactory redisConnectionFactory;
修改
(5)测试
启动工程,使用密码模式获取令牌:
查看redis:
SpringSecurity + OAuth2.0 + JWT
前面只使用Oauth2.0的话,颁发的通行令牌长度太短了,现在想整合JWT,将颁发的token转换一下,转换成jwt格式的长令牌。
JWT:JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简介的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使用RSA的公钥/私钥对来签名,防止被篡改。
整合JWT
直接在此工程的基础上修改了。
pom
(2)Redis配置类
注释掉Redis的配置类。
(3)Jwt配置类
在config包下创建JwtTokenStoreConfig配置类,如下:
package com.sec.kun.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;/*** @Author zhoukun* @Date 2021/2/17 19:50*/
@Configuration
public class JwtTokenStoreConfig {//注册JwtAccessTokenConverter@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter() {JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();//配置jwt秘钥jwtAccessTokenConverter.setSigningKey("zhoukun");return jwtAccessTokenConverter;}//注册TokenStore@Beanpublic TokenStore tokenStore() {return new JwtTokenStore(jwtAccessTokenConverter());}
}
(4)修改授权配置类
修改AuthorizationServerConfig类,如下:
/***Jwt配置类*/@AutowiredJwtAccessTokenConverter jwtAccessTokenConverter;@AutowiredTokenStore tokenStore;@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints)throws Exception {endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsServiceImpl).tokenStore(tokenStore).accessTokenConverter(jwtAccessTokenConverter);}
(5)测试
使用密码模式获取jwt令牌,如下:
现在的access_token令牌的长度发生了变化,与它对应的是jti值。解析这个token值:
扩展JWT的内容
现在想往JWT令牌中添加自定义的内容,过程如下。
(1)Jwt内容增强器
创建一个jwt包,包下创建一个Jwt的内容增强器JwtTokenEnhancer,如下:
package com.sec.kun.hancer;import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.stereotype.Component;import java.util.HashMap;
import java.util.Map;/*** @Author zhoukun* @Date 2021/2/17 19:56*/
@Component
public class JwtTokenEnhancer implements TokenEnhancer {@Overridepublic OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication oAuth2Authentication) {//自定义的内容存到map中Map<String,Object> map = new HashMap<>();map.put("city","西安");map.put("like","yu");map.put("age",20);map.put("name","泡泡茶壶");//下转型if(accessToken instanceof DefaultOAuth2AccessToken) {DefaultOAuth2AccessToken defaultOAuth2AccessToken = (DefaultOAuth2AccessToken)accessToken;defaultOAuth2AccessToken.setAdditionalInformation(map);return defaultOAuth2AccessToken;}return null;}
}
(2)修改Jwt配置类
修改Jwt配置类JwtTokenStoreConfig,如下:
//密码模式需要配置@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints)throws Exception {//创建TokenEnhancerChain实例TokenEnhancerChain tokenEnhancerChain=new TokenEnhancerChain();List<TokenEnhancer> enhancerList=new ArrayList<>();//配置jtv内容增强器enhancerList.add(jwtTokenEnhancer);enhancerList.add(jwtAccessTokenConverter);tokenEnhancerChain.setTokenEnhancers(enhancerList);endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsServiceImpl).tokenStore(tokenStore).accessTokenConverter(jwtAccessTokenConverter).tokenEnhancer(tokenEnhancerChain);}
(4)测试
解析生成的jwt令牌:
解析JWT的内容
JWT令牌的内容一般要在java程序中解析出来,以下演示过程。
(1)pom依赖
还是使用jjwt来解析。pom中添加依赖:
<!-- jjwt依赖 --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency>
(2)controller
修改UserController,如下:
@RequestMapping("/getCurrentUser")public Object getCurrentUser(Authentication authentication, HttpServletRequest request) {//获取请求头的指定内容String header = request.getHeader("Authorization");//截取,去掉请求头的前6位,获取tokenString token = header.substring(header.indexOf("bearer") + 7);//解析Token,获取Claims对象Claims claims = Jwts.parser().setSigningKey("zhoukun".getBytes(StandardCharsets.UTF_8)).parseClaimsJws(token).getBody();return claims;}
测试
获取令牌
带着令牌获取资源
获取到了,返回的是jwt令牌解析后的内容。
JWT刷新令牌
在Spring Cloud Security中使用oauth2时,如果令牌失效了,可以使用刷新令牌通过refresh_token的授权模式再次获取access_token,只需修改认证服务器的配置,添加refresh_token的授权模式即可。
修改授权服务配置类AuthorizationServerConfig,如下:
测试:1分钟后再发请求:
获取不到资源了,现在jwt通行令牌已经过期了。解决方法是加一个刷新令牌refresh_token。如下:
然后再启动工程获取令牌:
等1分钟,用通行令牌获取资源:
通行令牌过期了。现在使用刷新令牌直接从授权服务端获取新的通行令牌:
获取到了新的通行令牌和刷新令牌。同样的,通行令牌的有效期还是1分钟,刷新令牌是1小时,再用这个新的通行令牌获取资源:
资源获取成功。
Spring Security + OAuth2.0相关推荐
- Spring Security OAuth2.0认证授权知识概括
Spring Security OAuth2.0认证授权知识概括 安全框架基本概念 基于Session的认证方式 Spring Security简介 SpringSecurity详解 分布式系统认证方 ...
- Spring Security OAuth2.0认证授权五:用户信息扩展到jwt
历史文章 [Spring Security OAuth2.0认证授权一:框架搭建和认证测试] [Spring Security OAuth2.0认证授权二:搭建资源服务] [Spring Securi ...
- Spring Security OAuth2.0认证授权三:使用JWT令牌
历史文章 [Spring Security OAuth2.0认证授权一:框架搭建和认证测试] [Spring Security OAuth2.0认证授权二:搭建资源服务] 前面两篇文章详细讲解了如何基 ...
- Spring Security oauth2.0微信小程序登录
微信小程序前期开发准备,可以参考这篇文章微信小程序前期准备 1.学习过Spring Secrity oauth2.0的都知道,他有四种登录模式可以选择 authorization code(授权码模式 ...
- 从零开始超详细的Spring Security OAuth2.0实现分布式系统授权(注册中心+网关+认证授权服务(JWT令牌验证)+资源调用服务)
文章目录 一.OAuth2.0 1.介绍 2.例子 3.执行流程 二.Spring Cloud Security OAuth2 1.环境介绍 2.认证流程 三.整合分布式项目 1.技术方案 2.项目结 ...
- Spring Security OAuth2.0 token生成与刷新机制源码阅读
一.介绍 Spring Security Oauth2是目前市面上非常流行的实现了OAuth2.0协议的权限框架.本文会介绍其是如何获取token以及刷新token的. 二.AbstractEndPo ...
- Spring Security OAuth2.0认证授权
文章目录 1.基本概念 1.1.什么是认证 1.2 什么是会话 1.3什么是授权 1.4授权的数据模型 1.4 RBAC 1.4.1 基于角色的访问控制 2.基于Session的认证方式 3.整合案例 ...
- Spring Security OAuth2.0笔记
请求命中filter: spring-security-oauth2-client:5.3.5.RELEASE org.springframework.security.oauth2.client.w ...
- 搭建认证服务器 - Spring Security Oauth2.0 集成 Jwt 之 【密码认证流程】 总结
在搭建介绍流程之前,确保您已经搭建了一个 Eureka 注册中心,因为没有注册中心的话会报错(也有可能我搭建的认证服务器是我项目的一个子模块的原因):Request execution error. ...
最新文章
- php mpdf html 转pdf,使用 MPDF 将HTML转为PDF,然后将该PDF转为PNG图片的时候,中文报错... ......
- 求数组中的最大数,最小数。
- 怎样检查python环境是否安装好_如何搭建pytorch环境的方法步骤
- python的xlwt模块的常用方法
- xfce4桌面的标题栏都变成了透明的
- android记账本统计_记账送老婆啦!初音 蕾姆 随便挑随便选| iOS 安卓
- 什么 Leader 值得追随?
- 开源 java CMS - FreeCMS2.6 站点设置
- 解决Android学习之ScollView嵌套ListView和GridView问题
- 十六进制数用int吗_你真的精通C语言吗?来解这十道C语言迷题试试吧!
- python从入门到精通 清华大学出版社-清华大学出版社-图书详情-《Python语言程序设计》...
- java图片颜色取反色,照片底片模式
- ​每一页都是知识点,这本Flutter企业级实践指南太绝了
- 360的编码html怎么写,html5之meta charset网页字符编码简写
- 2017-3-01 test
- 行列式键盘+共阴极数码管显示
- IcedTea:首个100%兼容、开源的Java
- 运算放大器使用注意事项
- SAP FICO 第一节 后台配置
- 类似京东商城客户端应用源码
热门文章
- 难得五年来第一次暑假没有出海,即使最终没有逃过8月份的CPT外业
- base 64 转码解码 表情包emoji转码
- 51信用卡股价年初至今上浮5倍,引入银行背景高管担任行政总裁
- 浏览器 弹广告 android,android手机 UC浏览器 时不时跳出广告 是不是中毒了
- aspx连接mysql木马_让你变成ASP木马高手_安全教程_脚本之家
- 51cto的请看过来
- Microsoft Visual Studio Installer Projects下载缓慢下载不动的解决办法
- 解决双显卡无法安装Ubuntu问题(转载)
- 让工程师沉思的68个经典小故事
- 服务器如何选择备案产品类型?