Spring Security 生成token(十三)

说明

前后端分离的项目,需要后端认证成功之后,颁发令牌 token ,下次客户端在请求资源的时候,需要携带 access_token 参数。

Spring Security 生成 token 的基本思路是:

  • 表单认证之后,在 successHandler 中根据 Authentication 生成 token,响应给前端
  • 创建 Filter TokenAuthenticationFilter 当前请求参数中有参数 access_token,解析token,获取 Authentication 放入 SecurityContext 上下文中。

开发

添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.nimbusds</groupId>
    <artifactId>nimbus-jose-jwt</artifactId>
</dependency>
<dependency>
    <groupId>com.rick.common</groupId>
    <artifactId>sharp-common</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

token授权过滤器 TokenAuthenticationFilter.java

public class TokenAuthenticationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        String accessToken = request.getParameter("access_token");
        if (StringUtils.isBlank(accessToken)) {
            chain.doFilter(request, response);
            return;
        }

        try {
            Authentication authentication = JWTUtils.toAuthentication(accessToken);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            chain.doFilter(request, response);
        } catch (Exception e) {
            HttpServletResponseUtils.write(response, "application/json;charset=UTF-8"
                    , JsonUtils.toJson(ResultUtils.exception(403, e.getMessage())));
        }
    }
}

安全配置 WebSecurityConfig.java

@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled=true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests((requests) -> {
            ((ExpressionUrlAuthorizationConfigurer.AuthorizedUrl) requests.anyRequest()).authenticated();
        })
                .formLogin()
                // 1. 认证成功后将token响应给前端
                .successHandler((request, response, authentication) -> {
                    String token = JWTUtils.createToken(authentication);
                    HttpServletResponseUtils.write(response, "application/json;charset=UTF-8"
                            , JsonUtils.toJson(ResultUtils.success(token)));
                })
                .and().exceptionHandling().authenticationEntryPoint((request, response, e) -> {
            HttpServletResponseUtils.write(response, "application/json;charset=UTF-8"
                    , JsonUtils.toJson(ResultUtils.exception(403, e.getMessage())));
        })
                .and().exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
                HttpServletResponseUtils.write(response, "application/json;charset=UTF-8"
                        , JsonUtils.toJson(ResultUtils.exception(403, e.getMessage())));
            }
        }).and().addFilterBefore(new TokenAuthenticationFilter(), BasicAuthenticationFilter.class)
                .csrf().disable();

    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}
  • 添加token授权过滤器 TokenAuthenticationFilter
  • 表单认证之后,在 successHandler 中根据 Authentication 生成 token,响应给前端
  • 认证授权的异常处理

添加用户信息 application.yml

spring:
  security:
    user:
      name: rick
      password: 123456
      roles: ADMIN

API接口 IndexController.java

@RestController
public class IndexController {

    @GetMapping
    public String index() {
        return "index";
    }

    @GetMapping("admin")
    public String admin(Authentication authentication) {
        return "admin ==> ";
    }

    @GetMapping("p1")
    @PreAuthorize("hasRole('p1')")
    public String p1(Authentication authentication) {
        return authentication.getPrincipal().toString();
    }
}

测试

没有携带token

curl -X GET \

http://127.0.0.1:8080/admin

响应

{
    "success": false,
    "code": 403,
    "msg": "Full authentication is required to access this resource"
}

认证

curl -X POST \
  'http://127.0.0.1:8080/login?username=rick&password=123456'

响应

{
    "success": true,
    "code": 0,
    "msg": "OK",
    "data": "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC94aG9wZS50b3AiLCJzdWIiOiJvcmcuc3ByaW5nZnJhbWV3b3JrLnNlY3VyaXR5LmNvcmUudXNlcmRldGFpbHMuVXNlciBbVXNlcm5hbWU9cmljaywgUGFzc3dvcmQ9W1BST1RFQ1RFRF0sIEVuYWJsZWQ9dHJ1ZSwgQWNjb3VudE5vbkV4cGlyZWQ9dHJ1ZSwgY3JlZGVudGlhbHNOb25FeHBpcmVkPXRydWUsIEFjY291bnROb25Mb2NrZWQ9dHJ1ZSwgR3JhbnRlZCBBdXRob3JpdGllcz1bUk9MRV9BRE1JTl1dIiwiZXhwIjoxNjM0MzYyMzIxLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl19.e4-tJzixAkOk9xeoX9zkehYjaH_DmBPe9tho6cCtu6M"
}

携带token

curl -X GET \
  'http://127.0.0.1:8080/admin?access_token=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC94aG9wZS50b3AiLCJzdWIiOiJvcmcuc3ByaW5nZnJhbWV3b3JrLnNlY3VyaXR5LmNvcmUudXNlcmRldGFpbHMuVXNlciBbVXNlcm5hbWU9cmljaywgUGFzc3dvcmQ9W1BST1RFQ1RFRF0sIEVuYWJsZWQ9dHJ1ZSwgQWNjb3VudE5vbkV4cGlyZWQ9dHJ1ZSwgY3JlZGVudGlhbHNOb25FeHBpcmVkPXRydWUsIEFjY291bnROb25Mb2NrZWQ9dHJ1ZSwgR3JhbnRlZCBBdXRob3JpdGllcz1bUk9MRV9BRE1JTl1dIiwiZXhwIjoxNjM0MzYyMzIxLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl19.e4-tJzixAkOk9xeoX9zkehYjaH_DmBPe9tho6cCtu6M'

响应:token正确

admin ==> 

能正常访问资源信息。

响应:如果token错误

{
    "success": false,
    "code": 403,
    "msg": "无效的token"
}

访问没有权限的资源 /p

curl -X GET \
  'http://127.0.0.1:8080/p1?access_token=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC94aG9wZS50b3AiLCJzdWIiOiJvcmcuc3ByaW5nZnJhbWV3b3JrLnNlY3VyaXR5LmNvcmUudXNlcmRldGFpbHMuVXNlciBbVXNlcm5hbWU9cmljaywgUGFzc3dvcmQ9W1BST1RFQ1RFRF0sIEVuYWJsZWQ9dHJ1ZSwgQWNjb3VudE5vbkV4cGlyZWQ9dHJ1ZSwgY3JlZGVudGlhbHNOb25FeHBpcmVkPXRydWUsIEFjY291bnROb25Mb2NrZWQ9dHJ1ZSwgR3JhbnRlZCBBdXRob3JpdGllcz1bUk9MRV9BRE1JTl1dIiwiZXhwIjoxNjM0MzYyMzIxLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl19.e4-tJzixAkOk9xeoX9zkehYjaH_DmBPe9tho6cCtu6M'

响应

{
    "success": false,
    "code": 403,
    "msg": "Access is denied"
}

源代码:https://github.com/jkxyx205/spring-security-learn/tree/master/spring-security-token

Spring Security Oauth2手动生成OAuth2AccessToken(十二)

如果接入多种登录方式,如:平台用户名密码登录、手机验证码登录,第三方登录(微信、QQ)。可以在验证通过之后在 AuthenticationSuccessHandler 中 通过 Authentication 生成手动生成token。

  • TokenGenerator.java
@Component
public class TokenGenerator {

    @Autowired
    @Qualifier("tokenServices")
    private AuthorizationServerTokenServices authorizationServerTokenServices;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static final String ACCESS_TO_REFRESH = "access_to_refresh:";

    /**
     * 表单验证完成后创建token
     * @param authentication
     * @return
     */
    public OAuth2AccessToken createToken(Authentication authentication) {
        ClientDetails clientDetails = clientDetailsService.loadClientByClientId("c");
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) authentication;

        TokenRequest tokenRequest = tokenRequest();

        OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);

        OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, usernamePasswordAuthenticationToken);

        OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);

        return token;
    }

    /**
     * 刷新token
     * @param accessToken
     * @return
     */
    public OAuth2AccessToken refresh(String accessToken) {
        OAuth2AccessToken _accessToken = tokenStore.readAccessToken(accessToken);
        if (Objects.nonNull(_accessToken) && !_accessToken.isExpired()) {
            return _accessToken;
        }

        String refreshToken = stringRedisTemplate.opsForValue().get(ACCESS_TO_REFRESH + accessToken);
        return authorizationServerTokenServices.refreshAccessToken(refreshToken, tokenRequest());
    }


    private TokenRequest tokenRequest() {
        ClientDetails clientDetails = clientDetailsService.loadClientByClientId("c");

        TokenRequest tokenRequest = new TokenRequest(Collections.emptyMap(),
                clientDetails.getClientId(),
                clientDetails.getScope(),
                "all");

        return tokenRequest;
    }
}
  • 测试
@SpringBootTest
public class TokenGeneratorTest {

    @Autowired
    private TokenGenerator tokenGenerator;

    @Test
    public void testCreateToken() {
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken
                = new UsernamePasswordAuthenticationToken("rick", "123456"
                , AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN, p1, p2"));
        OAuth2AccessToken token = tokenGenerator.createToken(usernamePasswordAuthenticationToken);
        System.out.println(token.getExpiration());
        System.out.println(token);
    }

    @Test
    public void testRefresh() {
        String accessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzIl0sInVzZXJfbmFtZSI6InJpY2siLCJzY29wZSI6WyJhbGwiXSwiaGVsbG8iOiJ3b3JsZCIsImV4cCI6MTYzNDMwMjkyNCwiYXV0aG9yaXRpZXMiOlsicDEiLCJwMiIsIkFETUlOIl0sImp0aSI6Ik5HZ0hEYUZiZFdXeWEyTUN1ZDFGZ0N5T1gyRSIsImNsaWVudF9pZCI6ImMifQ.X0Y9jXHD4u9aszh9lXs0flnyuNBm3lJ2Swiy49TxSqw";
        OAuth2AccessToken token = tokenGenerator.refresh(accessToken);
        System.out.println(token.getValue().equals(accessToken));
    }
}

Spring Security Oauth2授权码登录+JWT token(十一)

说明

  • 授权码模式 authorization_code 需要进行登录页面验证,所以授权服务器和资源服务器需要分开部署
  • 密码模式 password 授权服务器和资源服务器可以部署在一起。

授权服务器

  • 依赖 pom.xml
<!-- spring security 4.2.15 -->
<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.5.5</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 资源服务器配置 AuthorizationServer.java
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

    @Autowired
    @Qualifier("tokenServices")
    private AuthorizationServerTokenServices authorizationServerTokenServices;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security
                .tokenKeyAccess("permitAll()") // oauth/token_key是公开
                .checkTokenAccess("permitAll()") // oauth/check_token公开
                .allowFormAuthenticationForClients(); //密码模式:表单认证(申请令牌)
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()// 使用in-memory存储
                .withClient("c")// client_id
                .secret("123456")//客户端密钥
                .resourceIds("res")//资源列表
                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")// 该client允许的授权类型authorization_code,password,refresh_token,implicit,client_credentials
                .scopes("all")// 允许的授权范围
                .autoApprove(false)//false跳转到授权页面
                //加上验证回调地址
                .redirectUris("http://www.baidu.com");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .userDetailsService(userDetailsService)
                .authenticationManager(authenticationManager)// 认证管理器 => 密码模式需要在认证服务器中设置 中配置AuthenticationManager
                .tokenServices(authorizationServerTokenServices)
                .authorizationCodeServices(authorizationCodeServices)
                .allowedTokenEndpointRequestMethods(HttpMethod.POST, HttpMethod.GET);
    }

    @Bean
    public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder) {
        List<AuthenticationProvider> providers = new ArrayList<>();
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(userDetailsService(passwordEncoder));
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
        providers.add(daoAuthenticationProvider);
        return new ProviderManager(providers);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
        //Admin Role
        UserDetails theUser = User.withUsername("rick")
                .password(passwordEncoder.encode("123456"))
                .roles("ADMIN", "p1").build();
        //User Role
        UserDetails theManager = User.withUsername("john")
                .password(passwordEncoder.encode("123456"))
                .roles("USER").build();
        InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
        userDetailsManager.createUser(theUser);
        userDetailsManager.createUser(theManager);
        return userDetailsManager;
    }

    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new InMemoryAuthorizationCodeServices();
    }
  • Token配置 TokenConfig.java
@Configuration
public class TokenConfig {

    private String SIGNING_KEY = "uaa123";

    @Bean
    public TokenStore tokenStore() {
        //使用内存存储令牌(普通令牌)
//        return new InMemoryTokenStore();
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
        return converter;
    }

    // 令牌管理服务
    @Bean("tokenServices")
    public AuthorizationServerTokenServices tokenService(ClientDetailsService clientDetailsService, TokenStore tokenStore) {
        DefaultTokenServices service=new DefaultTokenServices();
        service.setClientDetailsService(clientDetailsService);//客户端详情服务
        service.setSupportRefreshToken(true);//支持刷新令牌
        service.setTokenStore(tokenStore);//令牌存储策略

        // 令牌增强,支持Jwt令牌
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        // 注意顺序accessTokenConverter在最后
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
        service.setTokenEnhancer(tokenEnhancerChain);

        service.setReuseRefreshToken(false); // 只对"存储"的有效,jwt_stoken无效
        service.setAccessTokenValiditySeconds(60); // 令牌默认有效期2小时
        service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
        return service;
    }

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return (oAuth2AccessToken, oAuth2Authentication) -> {
            Map<String, Object> info = new HashMap<>();
            info.put("hello", "world");
            ((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(info);
            return oAuth2AccessToken;
        };
    }

}
  • 配置登录页面 WebSecurityConfig
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}
  • 配置文件 application.yml
server:
  port: 8081
  • 将token存储到 redis 中,需要以下两步
    添加 pom.xml 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

修改 TokenStore

@Bean
public TokenStore tokenStore(RedisConnectionFactory redisConnectionFactory) {
    return new RedisTokenStore(redisConnectionFactory);
}

源码地址:https://github.com/jkxyx205/spring-security-learn/tree/master/spring-security-oauth2/spring-security-oauth2-server

资源服务器

  • 依赖 pom.xml
<!-- spring security 4.2.15 -->
<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.5.5</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 资源服务配置 ResourceServer.java
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled=true, prePostEnabled = true)
public class ResourceServer extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        // 资源id
        resources.resourceId("res")
                .stateless(true)
                .accessDeniedHandler((request, response, e) -> {
                    response.getWriter().write(e.getMessage());
                });
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        ((ExpressionUrlAuthorizationConfigurer.AuthorizedUrl)http.authorizeRequests()
                .antMatchers("/**").access("#oauth2.hasScope('all')")
                .antMatchers("/admin").hasRole("ADMIN")
                .anyRequest()).authenticated();

    }
}

  • 检查token:方式一,采用DefaultTokenServices。 配置TokenStore(必须和AuthorizationServer相同,才能解析)TokenConfig.java
@Configuration
public class TokenConfig {

    private String SIGNING_KEY = "uaa123";

    @Bean
    public TokenStore tokenStore() {
        //使用内存存储令牌(普通令牌)
//        return new InMemoryTokenStore();
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
        return converter;
    }
}
  • 检查token:方式二,配置文件的方式,采用 RemoteTokenServices (推荐)
security:
  oauth2:
    client:
      client-id: c
      client-secret: 123456
      access-token-uri: http://localhost:8081/oauth/token
      user-authorization-uri: http://localhost:8081/oauth/authorize
    resource:
      token-info-uri: http://localhost:8081/oauth/check_token
  • API接口 IndexController.java
@RestController
public class IndexController {

    @GetMapping
    public String index() {
        return "index";
    }

    @GetMapping("admin")
    public String admin(Authentication authentication, @RequestParam("access_token") String accessToken, Jwt jwt) {
        return jwt.getClaims();
    }

    @GetMapping("p1")
    @PreAuthorize("hasRole('p1')")
    public String p1(Authentication authentication, @RequestParam("access_token") String accessToken) {
        Jwt jwt = JwtHelper.decode(accessToken);
        return jwt.getClaims();
    }
}
  • 配置文件 application.yml
server:
  port: 8082

源码地址:https://github.com/jkxyx205/spring-security-learn/tree/master/spring-security-oauth2/spring-security-oauth2-resource

测试

  • 获取授权码
http://localhost:8081/oauth/authorize?response_type=code&client_id=c&redirect_uri=http://www.baidu.com&scope=all

跳转到登录页面:输入用户名密码:rick/123456;允许授权,获取地址栏授权码 code

  • 根据授权码 code 获取token
curl -X POST \
  http://localhost:8081/oauth/token \
  -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
  -F code=cP8TeB \
  -F grant_type=authorization_code \
  -F client_id=c \
  -F client_secret=123456 \
  -F redirect_uri=http://www.baidu.com \
  -F scope=all

返回 access_token

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzIl0sInVzZXJfbmFtZSI6InJpY2siLCJzY29wZSI6WyJhbGwiXSwiaGVsbG8iOiJ3b3JsZCIsImV4cCI6MTYzNDI4NTg2MSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfcDEiXSwianRpIjoienFOUFZnQVpCeFdwaE9WM1hWZjNZazNkZklVIiwiY2xpZW50X2lkIjoiYyJ9.C7NQVGDgexoZ0VSpbfH_hw1eVXV7JBmB7EH9YydDQlg",
    "token_type": "bearer",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzIl0sInVzZXJfbmFtZSI6InJpY2siLCJzY29wZSI6WyJhbGwiXSwiYXRpIjoienFOUFZnQVpCeFdwaE9WM1hWZjNZazNkZklVIiwiaGVsbG8iOiJ3b3JsZCIsImV4cCI6MTYzNDU0NTAwMSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfcDEiXSwianRpIjoiRXdWOXMtYk9mdktGM1V0SFNUc2h3b285dGk0IiwiY2xpZW50X2lkIjoiYyJ9.AWTDhc8Sl4M4tTRC4AdJ1njyhQF5oAXTDNeIfAt3b1o",
    "expires_in": 59,
    "scope": "all",
    "hello": "world",
    "jti": "zqNPVgAZBxWphOV3XVf3Yk3dfIU"
}
  • 检查token
curl -X POST \
  'http://localhost:8081/oauth/check_token?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzIl0sInVzZXJfbmFtZSI6InJpY2siLCJzY29wZSI6WyJhbGwiXSwiaGVsbG8iOiJ3b3JsZCIsImV4cCI6MTYzNDI5MTkyNiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfcDEiXSwianRpIjoiRTgwajVrclhSNElHZmdvUmV1UXlrc29xQ2NvIiwiY2xpZW50X2lkIjoiYyJ9.xj_Ui_La2XjrUeSbufXunobiyzbevzPOppcknJig7d0'
  • 携带 access_token 获取资源
curl -X GET \
  'http://localhost:8082/p1?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzIl0sInVzZXJfbmFtZSI6InJpY2siLCJzY29wZSI6WyJhbGwiXSwiaGVsbG8iOiJ3b3JsZCIsImV4cCI6MTYzNDI4NjAyMSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfcDEiXSwianRpIjoibDVVcHF3UUlBWDBMRFVQYWg1T1Q1RDdrVFgwIiwiY2xpZW50X2lkIjoiYyJ9.pknFynXTl8koIUATnoShKbQSOZnwgmP8g4943lfHK7w'

sharp-database中在Controller中使用TableGridService获取数据

TableGridService 依赖于工具类 GridUtils,可以在 Controller 层,通过SQL构造对象,直接从 HttpServletRequest 获取请求参数。

@GetMapping("request")
public Grid request(HttpServletRequest request) {
    return new DefaultTableGridService("SELECT id, title, work_time FROM t_demo WHERE title like :title")
            .list(request);
}

也可以手动指定查询count的SQL,提高查询效率。

@GetMapping("request")
public Grid request(HttpServletRequest request) {
    return new DefaultTableGridService("SELECT id, title, work_time FROM t_demo WHERE title like :title",
    "SELECT count(*) FROM t_demo WHERE title like :title")
            .list(request);
}