在搭建介绍流程之前,确保您已经搭建了一个 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认证信息由我们服务端提供,步骤流程分为以下几步:

  1. 用户从页面输入账号密码,请求我们自己的 Controller 接口
  2. Controller 层调用 Service 层,Service 层调用 OAuth2.0 的认证地址 [/oauth/token]
  3. 进行密码授权认证操作,如果账号密码正确了,就返回令牌信息给 Service 层
  4. Service 将令牌信息给 Controller 层
  5. 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 之 【密码认证流程】 总结相关推荐

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

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

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

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

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

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

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

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

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

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

  6. Spring Security OAuth2.0认证授权

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

  7. Spring Security + OAuth2.0

    授权服务器 授权服务器中有4个端点.说明如下: Authorize Endpoint :授权端点,进行授权. Token Endpoint :令牌端点,经过授权拿到对应的Token. lntrospe ...

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

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

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

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

最新文章

  1. C++11新特性中的匿名函数Lambda表达式的汇编实现分析(二)
  2. dotNet中,取得指定日期所在月份的最后一天
  3. android判断模拟器的三种方法
  4. 网页title上面添加图片
  5. 超级楼梯HDOJ2041
  6. JZOJ 5697. 【gdoi2018 day1】农场(farm)
  7. Oracle 数据恢复指导具体解释
  8. tg2015 信息传递 (洛谷p2661)
  9. Mac OS使用技巧之十六:系统失去响应怎么办?
  10. Mac远程连接服务器
  11. 移动端自适应缩放代码
  12. 机器学习-KMeans聚类 K值以及初始类簇中心点的选取
  13. matlab app designer:夫琅禾费圆孔衍射仿真
  14. linux检测nfc,kali linux 能用哪些nfc读卡器
  15. 松下A6驱动面板操作参数设置与保存
  16. 影视后期制作(Pr)
  17. 假如我来架构12306网站(一) - 概论
  18. 4_竞赛无人机基本自动飞行支持函数与导航控制函数解析——零基础学习竞赛无人机搭积木式编程
  19. GetPrivateProfileString函数之新手上路
  20. php课设源代码网站,php精品课程教学网站在线发布系统

热门文章

  1. 【Smartform】开发中报SSFCOMPOSER154错误原因分析
  2. 以无代码的方式将Form打印内容转成生成PDF文件
  3. SAP导出Excel错点拒绝处理
  4. SAP实施方法论之探讨-现状调研与需求分析
  5. ABAP:List中回写CheckBox的值到内表
  6. php写的微信聊天界面,浅谈 聊天界面 核心架构设计
  7. git公有转私有_【IT新手之路】客户端组件化之私有库搭建
  8. android 窗口监听按键,Android编程实现Dialog窗体监听的方法
  9. larvel 中的api.php_Laravel API 系列教程(一): 基于 Laravel 5.5 构建 测试 RESTful API...
  10. java构造字符缓冲区_java学习笔记 | 学步园