授权服务器


授权服务器中有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相关推荐

  1. Spring Security OAuth2.0认证授权知识概括

    Spring Security OAuth2.0认证授权知识概括 安全框架基本概念 基于Session的认证方式 Spring Security简介 SpringSecurity详解 分布式系统认证方 ...

  2. Spring Security OAuth2.0认证授权五:用户信息扩展到jwt

    历史文章 [Spring Security OAuth2.0认证授权一:框架搭建和认证测试] [Spring Security OAuth2.0认证授权二:搭建资源服务] [Spring Securi ...

  3. Spring Security OAuth2.0认证授权三:使用JWT令牌

    历史文章 [Spring Security OAuth2.0认证授权一:框架搭建和认证测试] [Spring Security OAuth2.0认证授权二:搭建资源服务] 前面两篇文章详细讲解了如何基 ...

  4. Spring Security oauth2.0微信小程序登录

    微信小程序前期开发准备,可以参考这篇文章微信小程序前期准备 1.学习过Spring Secrity oauth2.0的都知道,他有四种登录模式可以选择 authorization code(授权码模式 ...

  5. 从零开始超详细的Spring Security OAuth2.0实现分布式系统授权(注册中心+网关+认证授权服务(JWT令牌验证)+资源调用服务)

    文章目录 一.OAuth2.0 1.介绍 2.例子 3.执行流程 二.Spring Cloud Security OAuth2 1.环境介绍 2.认证流程 三.整合分布式项目 1.技术方案 2.项目结 ...

  6. Spring Security OAuth2.0 token生成与刷新机制源码阅读

    一.介绍 Spring Security Oauth2是目前市面上非常流行的实现了OAuth2.0协议的权限框架.本文会介绍其是如何获取token以及刷新token的. 二.AbstractEndPo ...

  7. Spring Security OAuth2.0认证授权

    文章目录 1.基本概念 1.1.什么是认证 1.2 什么是会话 1.3什么是授权 1.4授权的数据模型 1.4 RBAC 1.4.1 基于角色的访问控制 2.基于Session的认证方式 3.整合案例 ...

  8. Spring Security OAuth2.0笔记

    请求命中filter: spring-security-oauth2-client:5.3.5.RELEASE org.springframework.security.oauth2.client.w ...

  9. 搭建认证服务器 - Spring Security Oauth2.0 集成 Jwt 之 【密码认证流程】 总结

    在搭建介绍流程之前,确保您已经搭建了一个 Eureka 注册中心,因为没有注册中心的话会报错(也有可能我搭建的认证服务器是我项目的一个子模块的原因):Request execution error. ...

最新文章

  1. php mpdf html 转pdf,使用 MPDF 将HTML转为PDF,然后将该PDF转为PNG图片的时候,中文报错... ......
  2. 求数组中的最大数,最小数。
  3. 怎样检查python环境是否安装好_如何搭建pytorch环境的方法步骤
  4. python的xlwt模块的常用方法
  5. xfce4桌面的标题栏都变成了透明的
  6. android记账本统计_记账送老婆啦!初音 蕾姆 随便挑随便选| iOS 安卓
  7. 什么 Leader 值得追随?
  8. 开源 java CMS - FreeCMS2.6 站点设置
  9. 解决Android学习之ScollView嵌套ListView和GridView问题
  10. 十六进制数用int吗_你真的精通C语言吗?来解这十道C语言迷题试试吧!
  11. python从入门到精通 清华大学出版社-清华大学出版社-图书详情-《Python语言程序设计》...
  12. java图片颜色取反色,照片底片模式
  13. ​每一页都是知识点,这本Flutter企业级实践指南太绝了
  14. 360的编码html怎么写,html5之meta charset网页字符编码简写
  15. 2017-3-01 test
  16. 行列式键盘+共阴极数码管显示
  17. IcedTea:首个100%兼容、开源的Java
  18. 运算放大器使用注意事项
  19. SAP FICO 第一节 后台配置
  20. 类似京东商城客户端应用源码

热门文章

  1. 难得五年来第一次暑假没有出海,即使最终没有逃过8月份的CPT外业
  2. base 64 转码解码 表情包emoji转码
  3. 51信用卡股价年初至今上浮5倍,引入银行背景高管担任行政总裁
  4. 浏览器 弹广告 android,android手机 UC浏览器 时不时跳出广告 是不是中毒了
  5. aspx连接mysql木马_让你变成ASP木马高手_安全教程_脚本之家
  6. 51cto的请看过来
  7. Microsoft Visual Studio Installer Projects下载缓慢下载不动的解决办法
  8. 解决双显卡无法安装Ubuntu问题(转载)
  9. 让工程师沉思的68个经典小故事
  10. 服务器如何选择备案产品类型?