# 码云第三方登录
**Repository Path**: chenhebin/expriment4
## Basic Information
- **Project Name**: 码云第三方登录
- **Description**: No description available
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-05-15
- **Last Updated**: 2021-04-14
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
##
东莞理工学院网络空间安全学院
#### 实验报告模板
课程名称:企业级开发框架专题
学期:2020春季
`实验名称`:利用Spring boot的自动装配特性实现动态注册组件 `实验序号`:四
`姓名`:陈和斌 `学号`:201741404201 `班级`:17软卓1班
`实验地址`:居家 `实验日期`:2020-5-18 `指导老师`:黎志雄
`教师评语`:XXX `实验成绩`:XXX `百分制`:XXX
`同组同学`:无
#### 一、实验目标
1、 掌握使用Spring Security框架;
2、 掌握配置Spring Security的安全过滤链;
3、 掌握编写Spring Security单元测试;
4、 掌握创建接入码云的应用;
5、 掌握码云OAuth2认证基本流程;
6、 掌握使用码云API;
7、 了解使用模板引擎或前端框架制作用户登录界面。
#### 二、实验环境
1、 JDK 1.8或更高版本
2、 Maven 3.6+
3、 IntelliJ IDEA
#### 三、实验内容与实验步骤
##### 1、 创建接入码云的应用。
#### `步骤`:
1). 在码云创建一个第三方应用,如下图所示:
2). 在idea中用静态变量将Client_ID和Client_Secret存起来,以便使用
##### 2、编写重定向过滤器的业务逻辑。
#### `步骤`:
在doFilterInternal()函数中添加代码如下:
```
UriComponents uriComponents = UriComponentsBuilder
.fromUriString("https://gitee.com/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code")
.build();
String uri = uriComponents.expand(CLIENT_ID, REDIRECT_URI).encode().toUriString();
response.sendRedirect(uri);
```
#### `说明`:
‘{}’是占位符,使用UriComponents动态生成uri;如果是/oauth2/gitee请求重定向到码云登录
##### 3、使用码云access_token API向码云认证服务器发送post请求获取access_token。
#### `步骤`:
在getAccessToken()函数中添加如下代码:
```
UriComponents uriComponents = UriComponentsBuilder.fromUriString(ACCESS_TOKEN_API_URI).build();
URI uri = uriComponents.expand(code,CLIENT_ID,REDIRECT_URI,CLIENT_SECRET).encode().toUri();
MultiValueMap header = new LinkedMultiValueMap<>();
header.add("User-Agent","Mozilla/5.0");
RequestEntity requestEntity = new RequestEntity(header,HttpMethod.POST,uri);
ResponseEntity responseEntity = rest.exchange(requestEntity,String.class);
JacksonJsonParser jacksonJsonParser = new JacksonJsonParser();
Map map = jacksonJsonParser.parseMap(responseEntity.getBody());
return map.get("access_token").toString();
```
#### `说明`:
先动态生成uri,构造一个带有"User_Agent"头部的post请求实体;
设置相应实体为String类型,接收相应内容;
返回的数据在响应体的body中,用JacksonJsonParser将json数据转换成java的map类型,从中提取所需的access_token并返回。
##### 4、 使用码云API获取授权用户的资料。
#### `步骤`:
在getUserInfo()函数中添加如下代码:
```
UriComponents uriComponents = UriComponentsBuilder.fromUriString(USER_INFO_URI).build();
URI uri = uriComponents.expand(accessToken).encode().toUri();
MultiValueMap header = new LinkedMultiValueMap<>();
header.add("User-Agent","Mozilla/5.0");
RequestEntity requestEntity = new RequestEntity(header,HttpMethod.GET,uri);
ResponseEntity responseEntity = rest.exchange(requestEntity,String.class);
JacksonJsonParser jacksonJsonParser = new JacksonJsonParser();
Map userInfo = jacksonJsonParser.parseMap(responseEntity.getBody());
return userInfo;
```
#### `说明`:
先动态生成uri,构造一个带有"User_Agent"头部的GET请求实体,注意如果是POST会报错;
设置相应实体为String类型,接收相应内容;
返回的数据在响应体的body中,用JacksonJsonParser将json数据转换成java的map类型,返回用户信息。
##### 5、把自定义的两个Filter加进安全过滤链。
#### `步骤`:
在configure()函数中添加如下代码:
```
GiteeOAuth2RedirectFilter giteeOAuth2RedirectFilter = new GiteeOAuth2RedirectFilter();
giteeOAuth2RedirectFilter = this.postProcess(giteeOAuth2RedirectFilter);
GiteeOAuth2LoginAuthenticationFilter giteeOAuth2LoginAuthenticationFilter = new GiteeOAuth2LoginAuthenticationFilter();
giteeOAuth2LoginAuthenticationFilter = this.postProcess(giteeOAuth2LoginAuthenticationFilter);
http.addFilterAfter(giteeOAuth2RedirectFilter,SecurityContextPersistenceFilter.class);
http.addFilterAfter(giteeOAuth2LoginAuthenticationFilter,GiteeOAuth2RedirectFilter.class);
```
#### `说明`:
先将过滤器事例用.postProcess()方法处理一下,完成依赖注入
然后将两个过滤器添加到过滤链中, giteeOAuth2RedirectFilter添加在SecurityContextPersistenceFilter后面
giteeOAuth2LoginAuthenticationFilter添加在giteeOAuth2RedirectFilter后面
##### 6、 把我们自定义的SecurityConfigurer应用到安全过滤链。
#### `步骤`:
1. 在configure()函数中添加两句代码,具体如下所示:
#### `说明`:
函数式编程应用自定义的GiteeOAuth2LoginConfigurer过滤链,需要实例化
##### 7. 改造/user接口,返回码云用户资料给前端;改造user.ftlh模板用于显示用户资料。
#### `步骤`:
1)添加一个存储用户信息的实体类CustomUserDetails
2)将用户信息类set到authenticationToken中
3)将用户信息存到model中:
```
@GetMapping("/user")
String userIndex(Model model) {
model.addAttribute("customUserDetails",SecurityContextHolder.getContext().getAuthentication().getDetails());
return "user";
}
```
4)前端展示用户信息:
```
${customUserDetails.login}
${customUserDetails.bio}
```
##### 8、 编写单元测试。模拟一个登录用户,访问受保护的接口/test,断言接口的返回内容body部分是否一致。
#### `步骤`:
编写test()方法如下所示:
```
@Test
@WithMockUser(username = "user",authorities = {"USER"})
public void test() throws Exception {
mvc.perform(get("/test").header("User-Agent","Mozilla/5.0"))
.andExpect(status().isOk())
.andExpect(content().string("访问/test接口成功,你拥有USER权限"));
}
```
说明:
1. @WithMockUser模拟了一个由"USER"权限的用户
2. 访问/test,断言返回的状态码是200,返回的数据是字符串"访问/test接口成功,你拥有USER权限"
#### 四、实验结果
##### 1、/test的测试结果
#### `说明`:
测试结果正确, /test接口的返回内容body部分一致
##### 2、前端效果展示
浏览器地址栏输入“localhost:8080/user”,点击码云登录的图标:
#### `说明`:
已经登录授权过的,token没过期前会直接跳过验证过程;否则中间还会有登录授权的过程
#### 五、实验总结
```
1.本次实验让我们最spring security有的初步的认识
2.积累了一点使用第三方登录的经验
```