搭建认证服务器 - Spring Security Oauth2.0 集成 Jwt 之 【密码认证流程】 总结
在搭建介绍流程之前,确保您已经搭建了一个 Eureka 注册中心,因为没有注册中心的话会报错(也有可能我搭建的认证服务器是我项目的一个子模块的原因):Request execution error. endpoint=DefaultEndpoint{ serviceUrl='http://localhost:8761/eureka/}
http://localhost:8761/eureka/ 是因为配置文件未提供注册中心地址,springcloud 默认的注册中心地址就是这个
另外:文末会提供所有代码
Oauth2.0 有以下四种授权模式:本文介绍 密码认证
1、授权码模式(Authorization Code)[常用]
2、隐式授权模式(Implicit)[不常用]
3、密码模式(Resource Owner Password Credentials)[常用]
4、客户端模式(Client Credentials)[不常用]
密码认证流程
密码模式(Resource Owner Password Credentials)与授权码模式的区别是申请令牌不再使用授权码,而是直接 通过用户名和密码即可申请令牌。
Post请求:http://localhost:9001/oauth/token
参数:
grant_type:密码模式授权填写password
username:账号
password:密码
并且此链接需要使用 http Basic认证。
所以密码认证流程相对来说比较简单的
前期工作:接下来的截图我默认你已经看过我上一篇博客 授权码认证流程,因为有些代码是在前一篇的基础上
在 UserDetailsServiceImpl 的 loadUserByUsername 方法后面
如下代码块
if(username == null){return null;
}
// 改成
if(username == null){return null;
}
String pwd = new BCryptPasswordEncoder().encode("qkm19981013");
String permissions = "permission1,permission2";
//创建User对象
return new UserJwt(username, pwd, AuthorityUtils.commaSeparatedStringToAuthorityList(permissions));
这样我们默认的账户是任意账户名,密码固定为 qkm19981013,接下来使用postman测试
基本上密码认证流程就是这样,但是这有一个问题,我们每次输入我们的用户名和密码的时候,我们都需要带上HttpBasic认证的客户端ID和密钥,但是实际上我们的ID和密钥是不允许透露出去的,这很不安全,所以接下来我们改造这种模式,客户端只需要提供用户名和密码,HttpBasic认证信息由我们服务端提供,步骤流程分为以下几步:
- 用户从页面输入账号密码,请求我们自己的 Controller 接口
- Controller 层调用 Service 层,Service 层调用 OAuth2.0 的认证地址 [/oauth/token]
- 进行密码授权认证操作,如果账号密码正确了,就返回令牌信息给 Service 层
- Service 将令牌信息给 Controller 层
- Controller层将数据存入到Cookie中,再响应用户.
Service 层业务
public interface AuthService {/*** 登录,授权认证方法*/AuthToken login(String username, String password, String clientId, String clientSecret);
}
@Service
public class AuthServiceImpl implements AuthService {@Autowiredprivate LoadBalancerClient loadBalancerClient;private RestTemplate restTemplate;private ClientHttpRequestFactory factory;@Autowiredpublic void setFactory() {SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();factory.setReadTimeout(5000);//msfactory.setConnectTimeout(15000);//msthis.factory = factory;}@Autowiredpublic void setRestTemplate() {this.restTemplate = new RestTemplate(this.factory);}@Overridepublic AuthToken login(String username, String password, String clientId, String clientSecret) {//申请令牌return applyToken(username,password,clientId, clientSecret);}/*** 认证方法* @param username:用户登录名字* @param password:用户密码* @param clientId:配置文件中的客户端ID* @param clientSecret:配置文件中的秘钥* @return AuthToken*/private AuthToken applyToken(String username, String password, String clientId, String clientSecret) {//选中认证服务的地址ServiceInstance serviceInstance = loadBalancerClient.choose("user-auth");if (serviceInstance == null) {throw new RuntimeException("找不到对应的服务");}//获取令牌的urlString path = serviceInstance.getUri().toString() + "/oauth/token";//定义bodyMultiValueMap<String, String> formData = new LinkedMultiValueMap<>();//授权方式formData.add("grant_type", "password");//账号formData.add("username", username);//密码formData.add("password", password);//定义头MultiValueMap<String, String> header = new LinkedMultiValueMap<>();header.add("Authorization", httpBasic(clientId, clientSecret));//指定 restTemplate当遇到400或401响应时候也不要抛出异常,也要正常返回值restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {@Overridepublic void handleError(@NonNull ClientHttpResponse response) throws IOException {//当响应的值为400或401时候也要正常响应,不要抛出异常if (response.getRawStatusCode() != 400 && response.getRawStatusCode() != 401) {super.handleError(response);}}});@SuppressWarnings("all")Map map;try {@SuppressWarnings("all")//http请求spring security的申请令牌接口ResponseEntity<Map> mapResponseEntity = restTemplate.exchange(path, HttpMethod.POST, new HttpEntity<>(formData, header), Map.class);//获取响应数据map = mapResponseEntity.getBody();} catch (RestClientException e) {throw new RuntimeException(e);}if (map == null || map.get("access_token") == null || map.get("refresh_token") == null || map.get("jti") == null) {//jti是jwt令牌的唯一标识作为用户身份令牌throw new RuntimeException("创建令牌失败!");}//将响应数据封装成AuthToken对象AuthToken authToken = new AuthToken();//访问令牌(jwt)String accessToken = (String) map.get("access_token");//刷新令牌(jwt)String refreshToken = (String) map.get("refresh_token");//jti,作为用户的身份标识String jwtToken = (String) map.get("jti");authToken.setJti(jwtToken);authToken.setAccessToken(accessToken);authToken.setRefreshToken(refreshToken);return authToken;}/*** base64编码* @param clientId 客户端ID* @param clientSecret 客户端密钥* @return String*/private String httpBasic(String clientId, String clientSecret) {//将客户端id和客户端密码拼接,按“客户端id:客户端密码”String string = clientId + ":" + clientSecret;//进行base64编码byte[] encode = Base64Utils.encode(string.getBytes());return "Basic " + new String(encode); //注意 "Basic " 英文后面有个空格}
}
//将客户端id和客户端密码拼接,按“客户端id:客户端密码” 关于为啥这样定义,且采用Base64 编码格式,如下图
- Basic 是明文,没有加密
- 我们将Basic后面的密文拷贝出来,使用 Base64 解密一下看看
Utils 工具
@Data
public class AuthToken implements Serializable{/*** 令牌信息*/String accessToken;/*** 刷新token(refresh_token)*/String refreshToken;/*** jwt短令牌*/String jti;}
public class CookieUtil {/*** 设置cookie** @param response response* @param name cookie名字* @param value cookie值* @param maxAge cookie生命周期 以秒为单位*/public static void addCookie(HttpServletResponse response, String domain, String path, String name,String value, int maxAge, boolean httpOnly) {Cookie cookie = new Cookie(name, value);cookie.setDomain(domain);cookie.setPath(path);cookie.setMaxAge(maxAge);cookie.setHttpOnly(httpOnly);response.addCookie(cookie);}/*** 根据cookie名称读取cookie* @param request request* @return map<cookieName,cookieValue>*/public static Map<String,String> readCookie(HttpServletRequest request, String ... cookieNames) {Map<String,String> cookieMap = new HashMap<String,String>();Cookie[] cookies = request.getCookies();if (cookies != null) {for (Cookie cookie : cookies) {String cookieName = cookie.getName();String cookieValue = cookie.getValue();for (String name : cookieNames) {if (name.equals(cookieName)) {cookieMap.put(cookieName, cookieValue);}}}}return cookieMap;}
}
/*** 继承了 org.springframework.security.core.userdetails.User,* 相当于对其的扩展,可以写我们需要添加的属性*/
@SuppressWarnings("unused")
public class UserJwt extends User {private String id; //用户IDprivate String name; //用户名字public UserJwt(String username, String password, Collection<? extends GrantedAuthority> authorities) {super(username, password, authorities);}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
VO 对象
@Data
public class LoginVo {@NotBlank(message = "用户名不能为空")private String username;@NotBlank(message = "密码不能为空")private String password;
}
Controller 层
@RestController
@RequestMapping(value = "/user")
public class AuthController {/*** 客户端ID*/@Value("${auth.clientId}")private String clientId;/*** 秘钥*/@Value("${auth.clientSecret}")private String clientSecret;/*** Cookie存储的域名*/@Value("${auth.cookieDomain}")private String cookieDomain;/*** Cookie生命周期*/@Value("${auth.cookieMaxAge}")private int cookieMaxAge;@Autowiredprivate AuthService authService;@PostMapping("/login")public Result<Object> login(@RequestBody @Validated LoginVo loginVo) {//申请令牌AuthToken authToken;try {authToken = authService.login(loginVo.getUsername(), loginVo.getPassword(), clientId, clientSecret);} catch (Exception e) {return new Result<>(false, StatusCode.ERROR, e.getMessage());}//用户身份令牌String access_token = authToken.getAccessToken();//将令牌存储到cookiesaveCookie(access_token);return new Result<>(true, StatusCode.OK, "登录成功!");}/*** 将令牌存储到cookie** @param token token*/private void saveCookie(String token) {HttpServletResponse response = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();assert response != null;CookieUtil.addCookie(response, cookieDomain, "/", "Authorization", token, cookieMaxAge, false);}
}
代码全部贴完了,接下来我们进行测试
再次发送请求
到此我们仅使用 用户名和密码 就进行了授权操作,保证了服务端的一定的安全性
搭建认证服务器 - Spring Security Oauth2.0 集成 Jwt 之 【密码认证流程】 总结相关推荐
- 搭建认证服务器 - Spring Security Oauth2.0 集成 Jwt 之 【授权码认证流程】 总结
在搭建介绍流程之前,确保您已经搭建了一个 Eureka 注册中心,因为没有注册中心的话会报错(也有可能我搭建的认证服务器是我项目的一个子模块的原因):Request execution error. ...
- Spring Security OAuth2.0认证授权三:使用JWT令牌
历史文章 [Spring Security OAuth2.0认证授权一:框架搭建和认证测试] [Spring Security OAuth2.0认证授权二:搭建资源服务] 前面两篇文章详细讲解了如何基 ...
- Spring Security OAuth2.0认证授权五:用户信息扩展到jwt
历史文章 [Spring Security OAuth2.0认证授权一:框架搭建和认证测试] [Spring Security OAuth2.0认证授权二:搭建资源服务] [Spring Securi ...
- Spring Security OAuth2.0认证授权知识概括
Spring Security OAuth2.0认证授权知识概括 安全框架基本概念 基于Session的认证方式 Spring Security简介 SpringSecurity详解 分布式系统认证方 ...
- 从零开始超详细的Spring Security OAuth2.0实现分布式系统授权(注册中心+网关+认证授权服务(JWT令牌验证)+资源调用服务)
文章目录 一.OAuth2.0 1.介绍 2.例子 3.执行流程 二.Spring Cloud Security OAuth2 1.环境介绍 2.认证流程 三.整合分布式项目 1.技术方案 2.项目结 ...
- 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
授权服务器 授权服务器中有4个端点.说明如下: Authorize Endpoint :授权端点,进行授权. Token Endpoint :令牌端点,经过授权拿到对应的Token. lntrospe ...
- Spring Security oauth2.0微信小程序登录
微信小程序前期开发准备,可以参考这篇文章微信小程序前期准备 1.学习过Spring Secrity oauth2.0的都知道,他有四种登录模式可以选择 authorization code(授权码模式 ...
- Spring Security OAuth2.0 token生成与刷新机制源码阅读
一.介绍 Spring Security Oauth2是目前市面上非常流行的实现了OAuth2.0协议的权限框架.本文会介绍其是如何获取token以及刷新token的. 二.AbstractEndPo ...
最新文章
- C++11新特性中的匿名函数Lambda表达式的汇编实现分析(二)
- dotNet中,取得指定日期所在月份的最后一天
- android判断模拟器的三种方法
- 网页title上面添加图片
- 超级楼梯HDOJ2041
- JZOJ 5697. 【gdoi2018 day1】农场(farm)
- Oracle 数据恢复指导具体解释
- tg2015 信息传递 (洛谷p2661)
- Mac OS使用技巧之十六:系统失去响应怎么办?
- Mac远程连接服务器
- 移动端自适应缩放代码
- 机器学习-KMeans聚类 K值以及初始类簇中心点的选取
- matlab app designer:夫琅禾费圆孔衍射仿真
- linux检测nfc,kali linux 能用哪些nfc读卡器
- 松下A6驱动面板操作参数设置与保存
- 影视后期制作(Pr)
- 假如我来架构12306网站(一) - 概论
- 4_竞赛无人机基本自动飞行支持函数与导航控制函数解析——零基础学习竞赛无人机搭积木式编程
- GetPrivateProfileString函数之新手上路
- php课设源代码网站,php精品课程教学网站在线发布系统
热门文章
- 【Smartform】开发中报SSFCOMPOSER154错误原因分析
- 以无代码的方式将Form打印内容转成生成PDF文件
- SAP导出Excel错点拒绝处理
- SAP实施方法论之探讨-现状调研与需求分析
- ABAP:List中回写CheckBox的值到内表
- php写的微信聊天界面,浅谈 聊天界面 核心架构设计
- git公有转私有_【IT新手之路】客户端组件化之私有库搭建
- android 窗口监听按键,Android编程实现Dialog窗体监听的方法
- larvel 中的api.php_Laravel API 系列教程(一): 基于 Laravel 5.5 构建 测试 RESTful API...
- java构造字符缓冲区_java学习笔记 | 学步园