乐趣区

关于java:微服务网关ZuulSpring-securityOauth20Jwt-动态盐值-实现权限控制开放接口平台5

前言

后面咱们曾经实现了生成 access_token, 上面咱们看如何把 access_token 转换成 jwt 的形式给到第三方。

调整

保留策略改成 JWT 即可
AuthServerConfig:OAuth2 的受权服务: 次要作用是 OAuth2 的客户端进行认证与受权

package com.baba.security.auth2.auth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

/**
 * OAuth2 的受权服务: 次要作用是 OAuth2 的客户端进行认证与受权
 * @Author wulongbo
 * @Date 2021/11/26 9:59
 * @Version 1.0
 */
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    @Qualifier("memberUserDetailsService")
    public UserDetailsService userDetailsService;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenStore jwtTokenStore;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private TokenEnhancer jwtTokenEnhancer;

    /**
     * 配置 OAuth2 的客户端信息:clientId、client_secret、authorization_type、redirect_url 等。* 理论保留在数据库中
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.jdbc(dataSource);
    }

    /**
     * 1. 减少 jwt 加强模式
     * 2. 调用 userDetailsService 实现 UserDetailsService 接口, 对客户端信息进行认证与受权
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        /**
         * jwt 加强模式
         * 对令牌的加强操作就在 enhance 办法中
         * 上面在配置类中, 将 TokenEnhancer 和 JwtAccessConverter 加到一个 enhancerChain 中
         *
         * 艰深点讲它做了两件事:* 给 JWT 令牌中设置附加信息和 jti:jwt 的惟一身份标识, 次要用来作为一次性 token, 从而回避重放攻打
         * 判断申请中是否有 refreshToken, 如果有, 就从新设置 refreshToken 并退出附加信息
         */
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> enhancerList = new ArrayList<TokenEnhancer>();
        enhancerList.add(jwtTokenEnhancer);
        enhancerList.add(jwtAccessTokenConverter);
        enhancerChain.setTokenEnhancers(enhancerList); // 将自定义 Enhancer 退出 EnhancerChain 的 delegates 数组中
        endpoints.tokenStore(jwtTokenStore)
                .userDetailsService(userDetailsService)
                /**
                 * 反对 password 模式
                 */
                .authenticationManager(authenticationManager)
                .tokenEnhancer(enhancerChain)
                .accessTokenConverter(jwtAccessTokenConverter);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                .tokenKeyAccess("permitAll()")
//                .checkTokenAccess("isAuthenticated()")
                // 解决 /oauth/check_token 无法访问的问题
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();}

}

ResServerConfig:基于 OAuth2 的资源服务配置类

package com.baba.security.auth2.auth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
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;
import org.springframework.security.oauth2.provider.token.TokenStore;

/**
 * ******** 在理论我的项目中此资源服务能够独自提取到资源服务项目中应用 ********
 * <p>
 * OAuth2 的资源服务配置类(次要作用是配置资源受爱护的 OAuth2 策略)
 * 注:技术架构通常上将用户与客户端的认证受权服务设计在一个子系统 (工程) 中, 而资源服务设计为另一个子系统(工程)
 *
 * @Author wulongbo
 * @Date 2021/11/26 10:01
 * @Version 1.0
 */
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    private TokenStore jwtTokenStore;

    /**
     * 同认证受权服务配置 jwtTokenStore - 独自剥离服务须要开启正文
     * @return
     */
//    @Bean
//    public TokenStore jwtTokenStore() {//        return new JwtTokenStore(jwtAccessTokenConverter());
//    }

    /**
     * 同认证受权服务配置 jwtAccessTokenConverter  - 独自剥离服务须要开启正文
     * 须要和认证受权服务设置的 jwt 签名雷同: "demo"
     *
     * @return
     */
//    @Bean
//    public JwtAccessTokenConverter jwtAccessTokenConverter() {//        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
//        accessTokenConverter.setSigningKey(Oauth2Constant.JWT_SIGNING_KEY);
//        accessTokenConverter.setVerifierKey(Oauth2Constant.JWT_SIGNING_KEY);
//        return accessTokenConverter;
//    }
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {resources.tokenStore(jwtTokenStore);
    }


    /**
     * 配置受 OAuth2 爱护的 URL 资源。* 留神: 必须配置 sessionManagement(), 否则拜访受护资源申请不会被 OAuth2 的拦截器
     * ClientCredentialsTokenEndpointFilter 与 OAuth2AuthenticationProcessingFilter 拦挡,
     * 也就是说, 没有配置的话, 资源没有受到 OAuth2 的爱护。*
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        /*
         留神:1、必须先加上:.requestMatchers().antMatchers(...),示意对资源进行爱护,也就是说,在拜访前要进行 OAuth 认证。2、接着:拜访受爱护的资源时,要具备哪里权限。------------------------------------
         否则,申请只是被 Security 的拦截器拦挡,申请基本到不了 OAuth2 的拦截器。------------------------------------
         requestMatchers()局部阐明:Invoking requestMatchers() will not override previous invocations of ::
         mvcMatcher(String)}, requestMatchers(), antMatcher(String), regexMatcher(String), and requestMatcher(RequestMatcher).
         */
        http
                // Since we want the protected resources to be accessible in the UI as well we need
                // session creation to be allowed (it's disabled by default in 2.0.6)
                // 另外,如果不设置,那么在通过浏览器拜访被爱护的任何资源时,每次是不同的 SessionID,并且将每次申请的历史都记录在 OAuth2Authentication 的 details 的中
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and()
                .requestMatchers()
                .antMatchers("/user", "/res/**","/home/**")
                .and()
                .authorizeRequests()
                .anyRequest().authenticated();
//                .antMatchers("/user", "/res/**","/home/**")
//                .authenticated();}
}

SecurityConfig:其中 sourceService 就是后面的 PermissionService 革新一下,针对客户端对外的所有凋谢接口进行权限管制,所以表辨别了一下。

package com.baba.security.auth2.auth;

import com.baba.security.auth2.entity.PermissionEntity;
import com.baba.security.auth2.entity.Source;
import com.baba.security.auth2.service.PermissionService;
import com.baba.security.auth2.service.SourceService;
import com.baba.security.auth2.service.impl.MemberUserDetailsService;
import com.baba.security.common.utils.MD5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 配置咱们 httpBasic 登陆账号和明码
 */
@Component
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SourceService sourceService;

    @Autowired
    private MemberUserDetailsService memberUserDetailsService;


    /**
     * 反对 password 模式(配置)
     * @return
     * @throws Exception
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();
    }

    /**
     * 引入明码加密类
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth.
//                inMemoryAuthentication()
//                .withUser("mayikt")
//                .password(passwordEncoder().encode("654321"))
//                .authorities("/*");
//
//        auth.
//                inMemoryAuthentication()
//                .withUser("baidu")
//                .password(passwordEncoder().encode("54321"))
//                .authorities("/*");
//        System.out.println("===============================");
//        System.out.println(passwordEncoder().encode("123456"));
//        System.out.println(new BCryptPasswordEncoder().encode("12345"));
        auth.userDetailsService(memberUserDetailsService).passwordEncoder(new PasswordEncoder() {
            /**
             * 对明码 MD5
             * @param rawPassword
             * @return
             */
            @Override
            public String encode(CharSequence rawPassword) {return MD5Util.encode((String) rawPassword);
            }

            /**
             * rawPassword 用户输出的明码
             * encodedPassword 数据库 DB 的明码
             * @param rawPassword
             * @param encodedPassword
             * @return
             */
            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {String rawPass = MD5Util.encode((String) rawPassword);
                boolean result = rawPass.equals(encodedPassword);
                return result;
            }
        });

    }


    /**
     * 配置 URL 拜访受权, 必须配置 authorizeRequests(), 否则启动报错, 说是没有启用 security 技术。* 留神: 在这里的身份进行认证与受权没有波及到 OAuth 的技术:当拜访要受权的 URL 时, 申请会被 DelegatingFilterProxy 拦挡,
     *      如果还没有受权, 申请就会被重定向到登录界面。在登录胜利 (身份认证并受权) 后, 申请被重定向至之前拜访的 URL。* @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {//        http.formLogin() // 注销界面,默认是 permit All
//                .and()
//                .authorizeRequests().antMatchers("/","/home").permitAll() // 不必身份认证能够拜访
//                .and()
//                .authorizeRequests().anyRequest().authenticated() // 其它的申请要求必须有身份认证
//                .and()
//                .csrf() // 避免 CSRF(跨站申请伪造)配置
//                .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize")).disable();


        Source source = new Source();
        List<Source> allPermission = sourceService.findByAll(source);
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry
                expressionInterceptUrlRegistry = http.authorizeRequests();
        allPermission.forEach((s) -> {expressionInterceptUrlRegistry.antMatchers(s.getUrl()).
                    hasRole(s.getTag());

        });
        http
                .formLogin()
                .and()
//                .authorizeRequests().antMatchers("/","/home").permitAll() // 不必身份认证能够拜访
//                .and()
                // 容许不登陆就能够拜访的办法,多个用逗号分隔
                .authorizeRequests()
                // 跨域申请会先进行一次 options 申请
//                .antMatchers(HttpMethod.OPTIONS).permitAll()
//                .antMatchers("/","/home").permitAll() // 不必身份认证能够拜访
                // 其余的须要受权后拜访
                .antMatchers("/**").authenticated() // 其它的申请要求必须有身份认证
//                .anyRequest().authenticated() // 其它的申请要求必须有身份认证
//                // 加一句这个
                .and()
                .csrf().disable(); // 关跨域爱护
    }

}

Oauth2Constant :当然签名能够改动静

package com.baba.security.auth2.constant;

/**
 * @Author wulongbo
 * @Date 2021/11/26 9:56
 * @Version 1.0
 */
public class Oauth2Constant {
    /**************************************Oauth2 参数配置 **********************************************/
    /**
     * JWT_SIGNING_KEY
     */
    public static final String JWT_SIGNING_KEY = "jwtsigningkey";
}

JWTokenEnhancer:自定义 TokenEnhancer

package com.baba.security.auth2.config;

import com.baba.security.auth2.entity.User;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;

import java.util.HashMap;
import java.util.Map;

/**
 * TokenEnhancer:在 AuthorizationServerTokenServices 实现存储拜访令牌之前加强拜访令牌的策略。* 自定义 TokenEnhancer 的代码:把附加信息退出 oAuth2AccessToken 中
 *
 * @Author wulongbo
 * @Date 2021/11/26 9:58
 * @Version 1.0
 */
public class JWTokenEnhancer implements TokenEnhancer {

    /**
     * 重写 enhance 办法, 将附加信息退出 oAuth2AccessToken 中
     *
     * @param oAuth2AccessToken
     * @param oAuth2Authentication
     * @return
     */
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {Map<String, Object> map = new HashMap<String, Object>();
        User user = (User)oAuth2Authentication.getPrincipal();
        map.put("id", user.getId());
        map.put("jwt-ext", "把把智能科技");
        ((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(map);
        return oAuth2AccessToken;
    }
}

JwtTokenConfig:JwtTokenConfig 配置类

package com.baba.security.auth2.config;

import com.baba.security.auth2.constant.Oauth2Constant;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

/**
 * JwtTokenConfig 配置类
 * 应用 TokenStore 将引入 JwtTokenStore
 *
 * 注:Spring-Sceurity 应用 TokenEnhancer 和 JwtAccessConverter 加强 jwt 令牌
 * @author wulongbo
 * @date 2021-11-27
 */
@Configuration
public class JwtTokenConfig {

    @Bean
    public TokenStore jwtTokenStore() {return new JwtTokenStore(jwtAccessTokenConverter());
    }

    /**
     * JwtAccessTokenConverter:TokenEnhancer 的子类, 帮忙程序在 JWT 编码的令牌值和 OAuth 身份验证信息之间进行转换(在两个方向上), 同时充当 TokenEnhancer 授予令牌的工夫。* 自定义的 JwtAccessTokenConverter:把本人设置的 jwt 签名退出 accessTokenConverter 中(这里设置 'demo', 我的项目可将此在配置文件设置)
     * @return
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
//        accessTokenConverter.setVerifierKey(Oauth2Constant.JWT_SIGNING_KEY);
        accessTokenConverter.setSigningKey(Oauth2Constant.JWT_SIGNING_KEY);
        return accessTokenConverter;
    }

    /**
     * 引入自定义 JWTokenEnhancer:
     * 自定义 JWTokenEnhancer 实现 TokenEnhancer 并重写 enhance 办法, 将附加信息退出 oAuth2AccessToken 中
     * @return
     */
    @Bean
    public TokenEnhancer jwtTokenEnhancer(){return new JWTokenEnhancer();
    }
}

GetSecret (获取 Header 以及加密后的明码)

package com.baba.security.auth2.utils;

import org.apache.commons.codec.binary.Base64;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import java.nio.charset.Charset;

/**
 * (获取 Header 以及加密后的明码)
 * @Author wulongbo
 * @Date 2021/11/26 10:50
 * @Version 1.0
 */
public class GetSecret {

    /**
     * 对应数据库中的 client_id 的值
     */
    private static final String APP_KEY = "mayikt_appid";
    /**
     * 对应数据库中的 client_secret 的值
     */
    private static final String SECRET_KEY = "123456";

    /**
     * main 办法执行程序获取到数据库中加密后的 client_secret 和申请头中的 getHeader
     * @param args
     */
    public static void main(String[] args){System.out.println();

        System.out.println("client_secret:"+new BCryptPasswordEncoder().encode(SECRET_KEY));

        System.out.println("getHeader:"+getHeader());

    }

    /**
     * 结构 Basic Auth 认证头信息
     *
     * @return
     */
    private static String getHeader() {
        String auth = APP_KEY + ":" + SECRET_KEY;
        byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(Charset.forName("US-ASCII")));
        String authHeader = "Basic" + new String(encodedAuth);
        return authHeader;
    }
}

pom 这里不再应用 cloud,应用 boot 的:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>zuul-auth</artifactId>
        <groupId>com.babaznkj.com</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>auth2</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.babaznkj.com</groupId>
            <artifactId>common</artifactId>
        </dependency>

        <!-- mysql 驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <!-- mybatis 启动器 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.starter.version}</version>
        </dependency>

        <!-- alibaba 的 druid 数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.starter.version}</version>
        </dependency>

        <!-- spring-boot -->
        <!-- 此 oauth2 依赖为 spingcloud 依赖, 在 springboot 中不要应用 -->
        <!-- 不是 starter, 手动配置 -->
<!--        <dependency>-->
<!--            <groupId>org.springframework.security.oauth</groupId>-->
<!--            <artifactId>spring-security-oauth2</artifactId>-->
<!--            <version>2.3.4.RELEASE</version>-->
<!--        </dependency>-->
        <!--        </dependency>-->
        <!-- 此 oauth2 依赖为 spingcloud 依赖, 在 springboot 中不要应用 -->

        <!--MQTT-->


        <!-- 因为一些注解和 API 从 spring security5.0 中移除,所以须要导入上面的依赖包  -->
        <!-- spring-boot-oauth2 - 依赖于 springboot2.0 以上版本 -->
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-integration</artifactId>
            <!--            <version>2.4.0</version>-->
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.integration/spring-integration-core -->
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-core</artifactId>
            <!--            <version>5.4.1</version>-->
        </dependency>

        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-stream</artifactId>
            <!--            <version>5.4.1</version>-->
        </dependency>
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-mqtt</artifactId>
            <!--            <version>5.4.1</version>-->
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.1</version>
        </dependency>
    </dependencies>
</project>

OpenApiUtils

package com.baba.security.auth2.utils;

import com.baba.security.auth2.constant.Oauth2Constant;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.lang.Assert;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;

public class OpenApiUtils {

    /**
     * 获取用户 id
     *
     * @param request
     * @return
     */
    public static Long getUserId(HttpServletRequest request) {String accessToken= request.getHeader("access_token");
        Assert.hasText(accessToken, "accessToken parameter must not be empty or null");
        final Claims claims = Jwts.parser()
                .setSigningKey(Oauth2Constant.JWT_SIGNING_KEY.getBytes())
                .parseClaimsJws(accessToken)
                .getBody();
        Long id = Long.valueOf(claims.get("id").toString());
        return id;
    }

    /**
     * 是否过期
     *
     * @param request
     * @return
     */
    public static boolean isExpiration(HttpServletRequest request) {String accessToken= request.getHeader("access_token");
        Claims claims = Jwts.parser().setSigningKey(Oauth2Constant.JWT_SIGNING_KEY.getBytes()).parseClaimsJws(accessToken).getBody();
        return claims.getExpiration().before(new Date());
    }

}

测试拜访的 Controller:
HomeController

package com.baba.security.auth2.controller;

import com.baba.security.auth2.utils.OpenApiUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

/**
 * @Author wulongbo
 * @Date 2021/11/26 10:03
 * @Version 1.0
 */
@RestController
@RequestMapping("/home")
public class HomeController {@PreAuthorize("hasRole('/home/getHome')")
    @RequestMapping("/getHome")
    public String home(HttpServletRequest request) {Long id = OpenApiUtils.getUserId(request);
        return "home page" + id;
    }

    @PreAuthorize("hasRole('/home/getindex')")
    @RequestMapping("/getindex")
    public String index(HttpServletRequest request) {Long id = OpenApiUtils.getUserId(request);
        return "index page:" + id;
    }
}

ResController

package com.baba.security.auth2.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.security.Principal;

/**
 * @Author wulongbo
 * @Date 2021/11/26 10:04
 * @Version 1.0
 */

@RestController
public class ResController {@RequestMapping("/res/getMsg")
    public String getMsg(String msg, Principal principal) {//principal 中封装了客户端(用户,也就是 clientDetails,区别于 Security 的 UserDetails,其实 clientDetails 中也封装了 UserDetails),不是必须的参数,除非你想得到用户信息,才加上 principal。return "Get the msg:"+msg;
    }
}

UserController

package com.baba.security.auth2.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.security.Principal;

/**
 * 用户服务接口
 *
 * @author Tom
 * @date 2020-09-04
 */
@RestController
public class UserController {@RequestMapping("/user")
    public Principal user(Principal principal) {
        //principal 在通过 security 拦挡后,是 org.springframework.security.authentication.UsernamePasswordAuthenticationToken
        // 在经 OAuth2 拦挡后,是 OAuth2Authentication
        return principal;
    }
}

oauth_client_details 表调整 sql:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details`  (`client_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '主键, 必须惟一, 用于惟一标识每一个客户端',
  `resource_ids` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '客户端所能拜访的资源 id 汇合',
  `client_secret` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用于指定客户端的拜访密匙',
  `scope` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '指定客户端申请的权限范畴, 可选值包含 read,write,trust',
  `authorized_grant_types` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '指定客户端反对的 grant_type, 可选值包含 authorization_code,password,refresh_token,implicit,client_credentials',
  `web_server_redirect_uri` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '客户端的重定向 URI',
  `authorities` varbinary(6000) NULL DEFAULT NULL COMMENT '指定客户端所领有的 Spring Security 的权限值',
  `access_token_validity` int(11) NULL DEFAULT NULL COMMENT '设定客户端的 access_token 的无效工夫值',
  `refresh_token_validity` int(11) NULL DEFAULT NULL COMMENT '设定客户端的 refresh_token 的无效工夫值(单位: 秒), 可选, 若不设定值则应用默认的无效工夫值(60 * 60 * 24 * 30, 30 天).',
  `additional_information` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '这是一个预留的字段, 在 Oauth 的流程中没有理论的应用, 可选, 但若设置值, 必须是 JSON 格局的 数据',
  `autoapprove` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '设置用户是否主动 Approval 操作, 默认值为‘false’',
  PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
INSERT INTO `oauth_client_details` VALUES ('baidu', 'mayikt_resource,user,device', '$2a$10$32N/ptu3jY0WjFH0qLbcEO2ZcCg4gYCJvMbwmqzf84qNCcDFBLl4q', 'read,write', 'authorization_code', 'http://www.mayikt.com/callback', NULL, NULL, NULL, NULL, NULL);
INSERT INTO `oauth_client_details` VALUES ('mayikt_appid', NULL, '$2a$10$6/Sab9Au.CdKqyE4x0gr0OJZrzrcDCWZ9GLqMDF6KX.jHad5vlkeO', 'read,write,trust', 'authorization_code,refresh_token,client_credentials', 'http://localhost:8082/res/getMsg', NULL, 36000, 36000, NULL, '1');
INSERT INTO `oauth_client_details` VALUES ('zdfd', 'mayikt_resource', '$2a$10$6/Sab9Au.CdKqyE4x0gr0OJZrzrcDCWZ9GLqMDF6KX.jHad5vlkeO', 'read,write,trust', 'authorization_code', 'http://www.mayikt.com/callback', NULL, NULL, NULL, NULL, NULL);

SET FOREIGN_KEY_CHECKS = 1;

浏览器中输出如下地址:获取受权码 code:
http://localhost:8082/oauth/authorize?client_id=mayikt_appid&response_type=code&redirect_uri=http://localhost:8082/res/getMsg

咱们用户名为手机号,输出手机号 17343759359,明码 132465[后盾会 MD5 加密比对]

点击受权,获取受权码

依据受权码获取 access_token:
** 备注:**有的文章说这里申请头中需带 Authrization:加上工具类 GetSecret 生成的 Basic bWF5aWt0X2FwcGlkOjEyMzQ1Ng==, 然而我测试不加也行

localhost:8082/oauth/token
form-data 参数如下

参数 参数值
grant_type authorization_code
code ZXfgp3
client_id mayikt_appid
client_secret 123456
redirect_uri http://localhost:8082/res/getMsg

查看 access_token:
http://localhost:8082/oauth/c…

咱们拜访一下资源服务:
http://localhost:8082/res/get…

拜访咱们权限管制的 controller
http://localhost:8082/home/ge…

当然咱们的 17343759359 用户我给了所有权限,我登录一个没有 getindex 权限的用户 13549553864 时候,申请 /home/getindex 看一看【已免去后面的受权步骤】
http://localhost:8082/home/ge…
拜访失败

自此咱们实现了 jwt 形式来实现凋谢接口平台

退出移动版