本文主要开发一个客户端,token认证远程关联到上一篇讲解的认证服务器中,token获取和校验都在服务器中做。
本文还会使用swagger2框架,并在swagger框架中内嵌一个登陆页面,通过登陆页面来实现token获取,这样登陆成功后,在swagger上面访问接口,都会带上登陆成功返回的token,使例子更符合正在开发场景。
1.pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
<!-- spring-security-oauth2 -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
</dependencies>
2.配置资源拦截ResourceServerConfig,这里和第一篇不一样的是,配置了RemoteTokenServices,关联到远程认证服务器做token获取认证。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
@Configuration
@EnableResourceServer
/**
* 这里设置访问路径权限
*/
public class ResourceServerConfig extends ResourceServerConfigurerAdapter{
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN") //默认加上前缀ROLE_,如ROLE_ADMIN
.antMatchers("/user/**").hasAnyRole("ADMIN","USER")
.antMatchers("/swagger-ui.html").permitAll() //去掉关于swagger框架的拦截
.antMatchers("/webjars/**").permitAll() //去掉关于swagger框架的拦截
.antMatchers("/swagger-resources/**").permitAll() //去掉关于swagger框架的拦截
.antMatchers("/v2/**").permitAll() //去掉关于swagger框架的拦截
.anyRequest().authenticated();
}
//配置远程连接授权服务器,验证token
@Bean
public RemoteTokenServices tokenServices(){
RemoteTokenServices tokenServices = new RemoteTokenServices();
//配置远程认证服务器,用于获取token地址
tokenServices.setCheckTokenEndpointUrl("http://localhost:8888/oauth/check_token");
//配置认证服务器的clientId和clientSecret
tokenServices.setClientId("demoClient");
tokenServices.setClientSecret("demoSecret");
return tokenServices;
}
}
3.配置swagger,这里大部分都是swagger的配置,可以自行百度了解.
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spi.service.contexts.SecurityContextBuilder;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.ApiKeyVehicle;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.nio.file.Path;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
@Component
@EnableSwagger2
public class SwaggerConfig {
private String securitySchemaOAuth2 = "oauth2schema";
private AuthorizationScope authorizationScopeRead = new AuthorizationScope("read","read");
private AuthorizationScope authorizationScopeWrite = new AuthorizationScope("write","write");
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("swagger标题")
.version("1.0")
.description("write by wbb")
.build();
}
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.wbb.swagger2oauth2.controller"))
.paths(PathSelectors.any())
.build()
.useDefaultResponseMessages(false)
.globalResponseMessage(RequestMethod.POST, globalResponse())
.globalResponseMessage(RequestMethod.DELETE, globalResponse())
.globalResponseMessage(RequestMethod.PUT, globalResponse())
.globalResponseMessage(RequestMethod.GET, globalResponse())
.securitySchemes(newArrayList(securitySchema()))
.securityContexts(newArrayList(securityContext()));
}
private SecurityContext securityContext(){
return SecurityContext.builder()
.securityReferences(securityReference())
.forPaths(PathSelectors.any())
.build();
}
private List<SecurityReference> securityReference(){
AuthorizationScope[] scopes = new AuthorizationScope[2];
scopes[0] = authorizationScopeRead;
scopes[1] = authorizationScopeWrite;
return newArrayList(new SecurityReference(securitySchemaOAuth2,scopes));
}
private List<ResponseMessage> globalResponse() {
List<ResponseMessage> result = newArrayList();
result.add(new ResponseMessageBuilder().code(400).message("Bad Request").build());
result.add(new ResponseMessageBuilder().code(401).message("Unauthorized").build());
result.add(new ResponseMessageBuilder().code(403).message("Forbidden").build());
result.add(new ResponseMessageBuilder().code(404).message("Not Found").build());
result.add(new ResponseMessageBuilder().code(500).message("Internal Server Error").build());
return result;
}
@Bean
SecurityConfiguration security() {
return new SecurityConfiguration(
"demoClient", //认证服务器clientId
"demoSecret", //认证服务器clientSecret
"oauth2-resource",//认证服务器resourceIds
"swagger",
"apiKey",
ApiKeyVehicle.HEADER,
"api_key",
" " /*scope separator*/);
}
//token认证
private OAuth securitySchema() {
LoginEndpoint loginEndpoint = new LoginEndpoint("http://localhost:8888/oauth/authorize");
GrantType grantType = new ImplicitGrant(loginEndpoint, "access_token");
return new OAuth(securitySchemaOAuth2, newArrayList(authorizationScopeRead, authorizationScopeWrite), newArrayList(grantType));
}
}
4.Controller
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("")
public class MyController {
@RequestMapping(value = "/test",method = RequestMethod.GET)
public Object test(){
return "test";
}
@RequestMapping(value = "/user",method = RequestMethod.GET)
@PreAuthorize("hasAnyRole('ROLE_USER')")
public Object user(){
return "user";
}
@RequestMapping(value = "/user/a",method = RequestMethod.GET)
public Object user2(){
return "user";
}
@PreAuthorize("hasAnyRole('ROLE_ADMIN')")
@RequestMapping(value = "/admin",method = RequestMethod.GET)
public Object admin(){
return "admin";
}
@RequestMapping(value = "/admin/b",method = RequestMethod.GET)
public Object admin2(){
return "admin";
}
5.这样整个项目就写完了,直接启动,不过在启动前需要先启动认证服务器(在上一篇写过)。
启动后,打开http://localhost:9999/swagger-ui.html#/,会出现一个swagger页面
随便点开一个url访问,会报没有认证的错误,跟之前我们用postman访问接口一样
接着,点击右上角的Authorize按钮,会弹出一个窗口
选择多选框,再点击Authorize按钮,会跳转到一个授权页面
再点击Authorize,会跳转到登陆页面,我这边登陆页面比较简单,如果需要的话,可以自行加工。直接输入账号密码登陆,这个登陆账号密码和第二篇用postman访问登陆一样。
点击登陆,登陆后,会返回到swagger页面,蓝色表示已登陆,红色是未登录。
登陆的原因在于第二篇yml配置中的spring.security.oauth2.client.redirectUris参数,登陆成功后重定向到客户端的swagger页面,该参数可以配置多个客户端swagger地址,上一篇中就配置了两个,大家可以修改客户端地址,多启动几个客户端,测试一下是不是这个样子
再接着访问第一个admin接口,会报无权限,跟上一篇postman演示一样
接着再访问user接口,访问成功
演示到这里就结束了。这个客户端,是不是很简单,本文这里集成了swagger,如果不需要,去掉swagger的配置就好了,通过接口获取token,然后再带上token访问接口,就和之前用postman演示一样。
讲到这里,本篇文章到此也就结束了,如果文章中有问题,或者有一些不够严谨完善的地方,希望大家体谅体谅。欢迎大家留言,交流交流。
最好附上项目github地址