不是因为看到希望了才去坚持,而坚持了才知道没有希望。
前言
在Spring Security源码分析十一:Spring Security OAuth2整合JWT和Spring Boot 2.0 整合 Spring Security Oauth2中,我们都是使用Restlet Client - REST API Testing测试被Oauth2
保护的API
。在本章中,我们将展示如何使用MockMvc
测试Oauth2
的API
。
修改pom.xml
添加spring-security-test
依赖
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
</dependency>
修改MerryyouResourceServerConfig配置
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.formLogin()
.successHandler(appLoginInSuccessHandler)//登录成功处理器
.and()
.authorizeRequests()
.antMatchers("/user").hasRole("USER")
.antMatchers("/forbidden").hasRole("ADMIN")
.anyRequest().authenticated().and()
.csrf().disable();
// @formatter:ON
}
- 修改
MerryyouResourceServerConfig
配置,增加对指定路径的角色校验。 - 默认角色为
ROLE_USER
,详见MyUserDetailsService
@Component
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User(username, "123456", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
}
}
- 关于为何是
hasRole("USER")
而不是hasRole("ROLE_USER")
请参考:Spring Security源码分析十三:Spring Security 基于表达式的权限控制 -
@formatter:off
和@formatter:ON
此段代码不进行格式化
增加/user和/forbidden请求映射
@GetMapping("/user")
public Object getCurrentUser1(Authentication authentication, HttpServletRequest request) throws UnsupportedEncodingException {
log.info("【SecurityOauth2Application】 getCurrentUser1 authenticaiton={}", JsonUtil.toJson(authentication));
String header = request.getHeader("Authorization");
String token = StringUtils.substringAfter(header, "bearer ");
Claims claims = Jwts.parser().setSigningKey(oAuth2Properties.getJwtSigningKey().getBytes("UTF-8")).parseClaimsJws(token).getBody();
String blog = (String) claims.get("blog");
log.info("【SecurityOauth2Application】 getCurrentUser1 blog={}", blog);
return authentication;
}
@GetMapping("/forbidden")
public String getForbidden() {
return "forbidden";
}
-
/user
请求需要USER
角色 -
/forbidden
请求需要ADMIN
角色
增加测试类SecurityOauth2Test
@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = SecurityOauth2Application.class)
@Slf4j
public class Oauth2MvcTest {
@Autowired
private WebApplicationContext wac;
@Autowired
private FilterChainProxy springSecurityFilterChain;
private MockMvc mockMvc;
//clientId
final static String CLIENT_ID = "merryyou";
//clientSecret
final static String CLIENT_SECRET = "merryyou";
//用户名
final static String USERNAME = "admin";
//密码
final static String PASSWORD = "123456";
private static final String CONTENT_TYPE = "application/json;charset=UTF-8";
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).addFilter(springSecurityFilterChain).build();//初始化MockMvc对象,添加Security过滤器链
}
- 初始化
Oauth2
信息
obtainAccessToken
public String obtainAccessToken() throws Exception {
final MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "password");
params.add("client_id", CLIENT_ID);
params.add("username", USERNAME);
params.add("password", PASSWORD);
// @formatter:off
ResultActions result = mockMvc.perform(post("/oauth/token")
.params(params)
.with(httpBasic(CLIENT_ID, CLIENT_SECRET))
.accept(CONTENT_TYPE))
.andExpect(status().isOk())
.andExpect(content().contentType(CONTENT_TYPE));
// @formatter:on
String resultString = result.andReturn().getResponse().getContentAsString();
JacksonJsonParser jsonParser = new JacksonJsonParser();
// System.out.println(jsonParser.parseMap(resultString).get("access_token").toString());
return jsonParser.parseMap(resultString).get("access_token").toString();
}
测试obtainAccessToken
@Test
public void getAccessToken() throws Exception {
final String accessToken = obtainAccessToken();
log.info("access_token={}", accessToken);
}
控制台打印:
access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE1MjY0NjEwMzgsImJsb2ciOiJodHRwczovL2xvbmdmZWl6aGVuZy5naXRodWIuaW8vIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9VU0VSIl0sImp0aSI6ImE1MmE2NDI4LTcwNzctNDcwZC05M2MwLTc0ZWNlNjFhYTlkMCIsImNsaWVudF9pZCI6Im1lcnJ5eW91In0.CPmkZmfOkgDII29RMIoMO7ufAe5WFrQDB7SaMDKa128
UnauthorizedTest
/**
* 未授权 401
*
* @throws Exception
*/
@Test
public void UnauthorizedTest() throws Exception {
// mockMvc.perform(get("/user")).andExpect(status().isUnauthorized());
ResultActions actions = mockMvc.perform(get("/user"));
int status = actions.andReturn().getResponse().getStatus();
Assert.assertTrue(status == HttpStatus.UNAUTHORIZED.value());
}
- 未授权
401
forbiddenTest
/**
* 禁止访问 403
*
* @throws Exception
*/
@Test
public void forbiddenTest() throws Exception {
final String accessToken = obtainAccessToken();
log.info("access_token={}", accessToken);
mockMvc.perform(get("/forbidden").header("Authorization", "bearer " + accessToken)).andExpect(status().isForbidden());
}
- 禁止访问
403
accessTokenOk
/**
* 允许访问 200
*
* @throws Exception
*/
@Test
public void accessTokenOk() throws Exception {
final String accessToken = obtainAccessToken();
log.info("access_token={}", accessToken);
mockMvc.perform(get("/user").header("Authorization", "bearer " + accessToken)).andExpect(status().isOk());
}
- 允许访问
200
代码下载
- github:https://github.com/longfeizheng/security-oauth2
- gitee:https://gitee.com/merryyou/security-oauth2
推荐文章
- Java创建区块链系列
- Spring Security源码分析系列
- Spring Data Jpa 系列
- 【译】数据结构中关于树的一切(java版)
- SpringBoot+Docker+Git+Jenkins实现简易的持续集成和持续部署