# experiment4 **Repository Path**: Graycat-wj/experiment4 ## Basic Information - **Project Name**: experiment4 - **Description**: 企业框架实验4-码云OAuth2第三方登陆 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-05-18 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 实验四 基于Spring Security码云OAuth2认证 #### 课程名称:企业级开发框架专题 学期:2020春季
实验名称基于Spring Security码云OAuth2认证实验序号
姓 名林伟佳学 号201741404125班 级17软工1班
实验地点网上实验日期2020/05/14指导老师黎志雄
教师评语实验成绩 评阅教师
百分制
同组同学
#### 一、 实验目的 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) 步骤一:创建接入码云的应用。
参考码云的参考文档:[码云OAth2参考文档](https://gitee.com/api/v5/oauth_doc#/list-item-3)之后创建应用 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0518/131925_3eeb5da2_5723573.png "屏幕截图.png") 2) 步骤二:编写重定向过滤器的业务逻辑。
当用户访问/oauth2/gitee时,本重定向过滤器拦截请求,并将用户重定向到码云三方认证页面上。 ``` ////////////////////////////////////////////////////////////// /// 步骤二:编写重定向过滤器的业务逻辑。 /// 当用户访问/oauth2/gitee时,本重定向过滤器拦截请求,并将用户重定向到码云三方认证页面上。 String myPath = "https://gitee.com/oauth/authorize?client_id="+CLIENT_ID+"&redirect_uri="+REDIRECT_URI+"&response_type=code"; response.sendRedirect(myPath); ////////////////////////////////////////////////////////////// ``` 3) 步骤三:使用码云access_token API向码云认证服务器发送post请求获取access_token。
**注意** :可以用UriCompentsBuilder构造uri也可以直接拼接字符串得到。 ``` private String getAccessToken(String code) { //////////////////////////////////////////////////// /// 步骤三:使用码云access_token API向码云认证服务器发送post请求获取access_token。 // String request_url = UriComponentsBuilder.fromUriString(ACCESS_TOKEN_API_URI) // .buildAndExpand(code, CLIENT_ID, REDIRECT_URI, CLIENT_SECRET).toString(); String MY_ACCESS_TOKEN_API_URI = "https://gitee.com/oauth/token?grant_type=authorization_code&code="+code+"&client_id="+CLIENT_ID+"&redirect_uri="+REDIRECT_URI+"&client_secret="+CLIENT_SECRET; RequestEntity requestEntity = RequestEntity .post(URI.create(MY_ACCESS_TOKEN_API_URI)) .headers(httpHeaders -> httpHeaders.add("User-Agent","Graycat")) .build(); ResponseEntity response = rest.exchange(requestEntity, String.class); String result = response.getBody(); Map parseMap = new JacksonJsonParser(new ObjectMapper()).parseMap(result); System.out.println("access_token获取情况:"+(String) parseMap.get("access_token")); return (String) parseMap.get("access_token"); //////////////////////////////////////////////////// } ``` 4) 步骤四:使用码云API获取授权用户的资料。
码云参考文档:https://gitee.com/api/v5/swagger#/getV5User ``` private Map getUserInfo(String accessToken) { //////////////////////////////////////////////////// /// 步骤四:使用码云API获取授权用户的资料。 /// 参考:https://gitee.com/api/v5/swagger#/getV5User /// 可以用UriCompentsBuilder构造uri也可以直接拼接字符串得到。 // String String request_url = UriComponentsBuilder.fromUriString(USER_INFO_URI) // .buildAndExpand(access_token).toString(); String MY_USERINFO_URI = "https://gitee.com/api/v5/user?access_token="+accessToken; RequestEntity requestEntity = RequestEntity .get(URI.create(MY_USERINFO_URI)) .headers(httpHeaders -> httpHeaders.add("User-Agent","Graycat")) .build(); ResponseEntity response = rest.exchange(requestEntity, Map.class); Map result = response.getBody(); System.out.println("用户资料获取情况:"+result); return result; //////////////////////////////////////////////////// } ``` 5) 步骤五:把自定义的两个Filter加进安全过滤链。
注意:不要加在SecurityContextPersistenceFilter前面。 ``` public void configure(H http) { //////////////////////////////////////////////////////////////// /// 步骤五:把自定义的两个Filter加进安全过滤链 /// 注意:不要加在SecurityContextPersistenceFilter前面就行。 http.addFilterAfter(postProcess(new GiteeOAuth2RedirectFilter()), HeaderWriterFilter.class) .addFilterAfter(postProcess(new GiteeOAuth2LoginAuthenticationFilter()), HeaderWriterFilter.class); //////////////////////////////////////////////////////////////// } ``` 6) 步骤六:把我们自定义的SecurityConfigurer应用到安全过滤链。
``` protected void configure(HttpSecurity http) throws Exception { // 不指定path,本安全过滤链会匹配所有请求。 http .authorizeRequests() .antMatchers("/").permitAll()// 首页放行 .anyRequest().hasAnyAuthority("USER").and() .formLogin() .loginPage("/user/login_frontend").permitAll() .defaultSuccessUrl("/user").and() .logout() .logoutUrl("/user/logout").permitAll() .logoutSuccessUrl("/user/login_frontend?logout").and() // 自定义访问拒绝异常处理逻辑 .exceptionHandling().accessDeniedHandler(UserSecurityConfig::accessDeniedHandle) //////////////////////////////////////////////// /// 步骤六:把我们自定义的SecurityConfigurer应用到安全过滤链 .and() .apply(new GiteeOAuth2LoginConfigurer<>()) //////////////////////////////////////////////// ; } ``` 7) 步骤七:改造/user接口,返回码云用户资料给前端;
``` /** * 获取登录用户的资料接口 *

* @see Authentication * @see Principal * @see Model */ @GetMapping("/user") String userIndex( Model model ) { //////////////////////////////////// /// 步骤七:改造/user接口,返回码云用户资料给前端;改造user.ftlh模板用于显示用户资料。 Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); UserDetails user = (UserDetails) authentication.getPrincipal(); RequestEntity requestEntity = RequestEntity .get(URI.create("https://gitee.com/api/v5/users/"+user.getUsername())) .headers(httpHeaders -> httpHeaders.add("User-Agent","Graycat")) .build(); ResponseEntity response = rest.exchange(requestEntity, Map.class); model.addAttribute("userInfo",response.getBody()); //////////////////////////////////// return "user"; } ``` 改造user.ftlh模板用于显示用户资料。
展示如下:
![输入图片说明](https://images.gitee.com/uploads/images/2020/0518/134718_2c07d7ad_5723573.png "屏幕截图.png")
登陆页面 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0518/135945_b7ac7279_5723573.png "屏幕截图.png") 8) 步骤八:编写单元测试。模拟一个登录用户,访问受保护的接口/test,断言接口的返回内容body部分是否一致。 ``` /** * 测试限权接口 *

* 模拟一个登录用户,访问受保护的接口。 *

* @see WithMockUser * @see MockMvc#perform(RequestBuilder) * @see MockMvcRequestBuilders#get(String, Object...) */ @Test @WithMockUser(username = "user",authorities = "USER") public void test() throws Exception { //////////////////////////////////////////// /// 步骤八:模拟一个登录用户,访问受保护的接口/test,断言接口的返回内容body部分是否一致。 mvc.perform(MockMvcRequestBuilders.get("/test")) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().string("访问/test接口成功,你拥有USER权限")); //////////////////////////////////////////// } ``` 四、 实验要求
1、 实验项目push到码云的公开仓库,并把仓库网址登记在在线文档;
2、 从码云仓库下载项目的ZIP文件,并交由班长汇总;
3、 撰写实验报告,完成各个实验任务。各实验任务中的附图,是老师的演示代码,同学们应该模仿演示代码改为自己的代码。实验报告中必须完整描述各实验任务实现过程并附截图。
4、 网络空间安全学院实验报告模板(2019试行)
5、 实验四项目仓库登记表
6、 严禁抄袭。如果不想项目仓库给别人看到,可以设置为私有仓库,并把老师的码云账号(dgsai@vip.qq.com)加入到仓库开发者方便老师检查实验。