权限-Spring Security实现分布式系统授权(九)

分布式权限流程图

步骤

1、UAA认证服务负责认证授权。

2、所有请求经过 网关到达微服务

3、网关负责鉴权客户端以及请求转发

4、网关将token解析后传给微服务,微服务进行授权。

Eureka注册中心

新建eureka子模块

pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

application.yml

##单节点eureka
server:
  port: 7000 #指定运行端口

#eureka配置
eureka:
  instance:
    hostname: localhost #指定主机地址 表示eureka服务端的实例名称
  client:
    register-with-eureka: false #指定是否要注册到注册中心(默认true) false表示eureka服务不注册自己
    fetch-registry: false #指定是否要从注册中心获取服务(默认true)false就表明自己是注册中心,不需要注册
    service-url: #将默认的服务地址改为自己的地址
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

启动类

package org.dong.oauth.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }

}

修改uaa和order

uaa和order都需要添加的依赖如下

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

uaa和order都需要在application.yml中配置eureka,将自己注册到eureka

eureka:
  client:
    service-url:
      defaultZone: http://localhost:7000/eureka/

然后分别在他们的主启动类上加上@EnableEurekaClient这个注解表明自己是eureka的客服端

最后启动eureka,uaa和order。访问http://localhost:7000/,我们发现uaa和order就被注册到eureka中了。

搭建网关

网关整合 OAuth2.0 有两种思路,一种是认证服务器生成jwt令牌, 所有请求统一在网关层验证,判断权限等操作; 另一种是由各资源服务处理,网关只做请求转发。

我们选用第一种。我们把API网关作为OAuth2.0的资源服务器角色,实现接入客户端权限拦截、令牌解析并转发当

前登录用户信息(jsonToken)给微服务,这样下游微服务就不需要关心令牌格式解析以及OAuth2.0相关机制了。

API网关在认证授权体系里主要负责两件事:

(1)作为OAuth2.0的资源服务器角色,实现接入方权限拦截。

(2)令牌解析并转发当前登录用户信息(明文token)给微服务

微服务拿到明文token(明文token中包含登录用户的身份和权限信息)后也需要做两件事:

(1)用户授权拦截(看当前用户是否有权访问该资源)

(2)将用户信息存储进当前线程上下文(有利于后续业务逻辑随时获取当前用户信息)

新建getway子模块作为网关模块

pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <!--导入zuul依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--Hystrix依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <!--Ribbon-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>
    </dependencies>

application.yml

server:
  port: 9003

spring:
  application:
    name: gateway-server
  main:
    allow-bean-definition-overriding: true
  cloud:
    client:
      ipAddress: 127.0.0.1
zuul:
  retryable: true
  ignored-services: "*"
  add-host-header: true
  sensitive-headers: "*"
  routes:
    uaa-service:
      stripPrefix: false
      path: /uaa/**
    order-service:
      stripPrefix: false
      path: /order/**


eureka:
  client:
    service-url:
      defaultZone: http://localhost:7000/eureka/

ribbon:
  ReadTimeout: 5000
  ConnectTimeout: 5000
management:
  endpoints:
    web:
      exposure:
        include: refresh,health,info,env

启动类

package org.dong.oauth.getway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class GetWayService {
    public static void main(String[] args) {
        SpringApplication.run(GetWayService.class);
    }
}

其实网关也可以看作一个特殊的资源服务,相关配置如下

还是使用JWT的方式存储令牌

package org.dong.oauth.getway.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;

@Configuration
public class TokenConfig {
    //定义JWT令牌服务
    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;
    //密钥
    private String SIGNING_KEY = "uaa123";

    /**
     * 存储令牌的三种方式
     * 1. JdbcTokenStore(jdbc方式)
     * 2. JWTTokenStore(JWT方式)
     * 3. InMemoryTokenStore(内存方式,下面我们使用内存方式存储普通令牌)
     *
     * @return
     */
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

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

网关下面有一系列的微服务,都需要在网关进行相关配置(如uaa授权服务和order资源服务)

package org.dong.oauth.getway.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
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.TokenStore;

@Configuration
//表明是资源服务
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig {


    /**
     * 1. 资源id需要和uaa模块中AuthorizationServer配置的资源id一致
     * .resourceIds("res1")//客服端可以访问的资源id
     */
    public static final String RESOURCE_ID = "res1";

    /*** 统一认证服务(UAA) 资源拦截  */
    @Configuration
    @EnableResourceServer
    public class UAAServerConfig extends ResourceServerConfigurerAdapter {
        @Autowired
        private TokenStore tokenStore;

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources
                    .tokenStore(tokenStore)
                    .resourceId(RESOURCE_ID)
                    .stateless(true);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/uaa/**")
                    .permitAll();
        }
    }

    /*** 订单服务 order */
    @Configuration
    @EnableResourceServer
    public class OrderServerConfig extends ResourceServerConfigurerAdapter {
        @Autowired
        private TokenStore tokenStore;

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources
                    .tokenStore(tokenStore)
                    .resourceId(RESOURCE_ID)
                    .stateless(true);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/order/**")
                    .access("#oauth2.hasScope('ROLE_ADMIN')");
        }
    }
}

因为网关类似资源服务,所以也需要配置安全服务,所有的访问都需要经过网关,我们让其放行就可以了。

package org.dong.oauth.getway.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/**")
                .permitAll()
                .and()
                .csrf()
                .disable();
    }
}

网关需要解析token,然后将token转发给微服务。

package org.dong.oauth.getway.filter;

import com.alibaba.fastjson.JSON;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.dong.oauth.getway.common.EncryptUtil;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * token传递拦截
 */
public class AuthFilter  extends ZuulFilter {

    @Override
    public String filterType() {
        //请求之前进行拦截
        return "pre";
    }

    @Override
    public int filterOrder() {
        //0表示数字越小越优先
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    //主要是解析token获取用户的身份和权限
    @Override
    public Object run() throws ZuulException {
        //获取上下文
        RequestContext ctx = RequestContext.getCurrentContext();
        //从安全的上下文中拿到用户的身份对象
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if(!(authentication instanceof OAuth2Authentication)){
            // 无token访问网关内资源的情况,目 前仅有uua服务直接暴露
            return null;
        }

        OAuth2Authentication oauth2Authentication = (OAuth2Authentication)authentication;
        Authentication userAuthentication = oauth2Authentication.getUserAuthentication();
        //1 获取当前用户身份信息
        Object principal = userAuthentication.getPrincipal();
        //2 获取当前用户的权限
        List<String> authorities = new ArrayList();
        userAuthentication.getAuthorities().stream()
                .forEach(s ->authorities.add(((GrantedAuthority) s)
                .getAuthority()));

        OAuth2Request oAuth2Request = oauth2Authentication.getOAuth2Request();
        Map<String, String> requestParameters = oAuth2Request.getRequestParameters();
        Map<String,Object> jsonToken = new HashMap<>(requestParameters);
        if(userAuthentication != null){
            jsonToken.put("principal",userAuthentication.getName());
            jsonToken.put("authorities",authorities);
        }
        //3 将身份和权限等信息存放到json中,转发给微服务
        ctx.addZuulRequestHeader("jsonToken", EncryptUtil.encodeUTF8StringBase64(JSON.toJSONString(jsonToken)));
        return null;
    }
}

解析格式工具类

package org.dong.oauth.getway.common;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Base64;

public class EncryptUtil {
    private static final Logger logger = LoggerFactory.getLogger(EncryptUtil.class);

    public static String encodeBase64(byte[] bytes){
        String encoded = Base64.getEncoder().encodeToString(bytes);
        return encoded;
    }

    public static byte[]  decodeBase64(String str){
        byte[] bytes = null;
        bytes = Base64.getDecoder().decode(str);
        return bytes;
    }

    public static String encodeUTF8StringBase64(String str){
        String encoded = null;
        try {
            encoded = Base64.getEncoder().encodeToString(str.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            logger.warn("不支持的编码格式",e);
        }
        return encoded;

    }

    public static String  decodeUTF8StringBase64(String str){
        String decoded = null;
        byte[] bytes = Base64.getDecoder().decode(str);
        try {
            decoded = new String(bytes,"utf-8");
        }catch(UnsupportedEncodingException e){
            logger.warn("不支持的编码格式",e);
        }
        return decoded;
    }

    public static String encodeURL(String url) {
    	String encoded = null;
		try {
			encoded =  URLEncoder.encode(url, "utf-8");
		} catch (UnsupportedEncodingException e) {
			logger.warn("URLEncode失败", e);
		}
		return encoded;
	}


	public static String decodeURL(String url) {
    	String decoded = null;
		try {
			decoded = URLDecoder.decode(url, "utf-8");
		} catch (UnsupportedEncodingException e) {
			logger.warn("URLDecode失败", e);
		}
		return decoded;
	}

    public static void main(String [] args){
        String str = "abcd{'a':'b'}";
        String encoded = EncryptUtil.encodeUTF8StringBase64(str);
        String decoded = EncryptUtil.decodeUTF8StringBase64(encoded);
        System.out.println(str);
        System.out.println(encoded);
        System.out.println(decoded);

        String url = "== wo";
        String urlEncoded = EncryptUtil.encodeURL(url);
        String urlDecoded = EncryptUtil.decodeURL(urlEncoded);
        
        System.out.println(url);
        System.out.println(urlEncoded);
        System.out.println(urlDecoded);
    }


}

写一个类配置一下啊上面的filter

package org.dong.oauth.getway.config;

import org.dong.oauth.getway.filter.AuthFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class ZuulConfig {
    @Bean
    public AuthFilter preFileter() {
        return new AuthFilter();
    }

    @Bean
    public FilterRegistrationBean corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.setMaxAge(18000L);
        source.registerCorsConfiguration("/**", config);
        CorsFilter corsFilter = new CorsFilter(source);
        FilterRegistrationBean bean = new FilterRegistrationBean(corsFilter);
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
}

修改order

资源微服务需要接收来自网关的token,然后把它解析出来

controller

package org.dong.oauth.order.controller;

import org.dong.oauth.order.pojo.UserDTO;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {
    @GetMapping(value = "/r1")
    @PreAuthorize("hasAnyAuthority('p1')")
    public String r1(){
        UserDTO user = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        return user.getUsername()+"访问资源1";
    }

    @GetMapping(value = "/r2")
    @PreAuthorize("hasAnyAuthority('p2')")
    public String r2(){
        UserDTO user = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        return "访问资源2";
    }

    @GetMapping(value = "/r3")
    @PreAuthorize("hasAnyAuthority('p3')")
    public String r3(){
        return "访问资源3";
    }
}

解析token

package org.dong.oauth.order.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.dong.oauth.order.common.EncryptUtil;
import org.dong.oauth.order.pojo.UserDTO;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @ClassName TokenAuthenticationFilter
 * @Description TODO
 * @Author xsh
 * @Date 2020-06-13 13:41
 * @Version 1.0
 **/
@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {


    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        //解析出头中的token
        String token = httpServletRequest.getHeader("jsonToken");
        if(token!=null){
            String json = EncryptUtil.decodeUTF8StringBase64(token);
            //将token转成json对象
            JSONObject jsonObject = JSON.parseObject(json);
            //用户身份信息
//            UserDTO userDTO = new UserDTO();
//            String principal = jsonObject.getString("principal");
//            userDTO.setUsername(principal);
//            String principal = jsonObject.getString("principal");
            System.out.println(jsonObject.getString("principal"));

            UserDTO userDTO = new UserDTO();
            userDTO.setUsername(jsonObject.getString("principal"));
            //用户权限
            JSONArray authoritiesArray = jsonObject.getJSONArray("authorities");
            String[] authorities = authoritiesArray.toArray(new String[authoritiesArray.size()]);
            //将用户信息和权限填充 到用户身份token对象中
            UsernamePasswordAuthenticationToken authenticationToken
                    = new UsernamePasswordAuthenticationToken(userDTO,null, AuthorityUtils.createAuthorityList(authorities));
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
            //将authenticationToken填充到安全上下文
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);


        }
        filterChain.doFilter(httpServletRequest,httpServletResponse);

    }
}

上面需要的两个类如下

package org.dong.oauth.order.common;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Base64;

public class EncryptUtil {
    private static final Logger logger = LoggerFactory.getLogger(EncryptUtil.class);

    public static String encodeBase64(byte[] bytes){
        String encoded = Base64.getEncoder().encodeToString(bytes);
        return encoded;
    }

    public static byte[]  decodeBase64(String str){
        byte[] bytes = null;
        bytes = Base64.getDecoder().decode(str);
        return bytes;
    }

    public static String encodeUTF8StringBase64(String str){
        String encoded = null;
        try {
            encoded = Base64.getEncoder().encodeToString(str.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            logger.warn("不支持的编码格式",e);
        }
        return encoded;

    }

    public static String  decodeUTF8StringBase64(String str){
        String decoded = null;
        byte[] bytes = Base64.getDecoder().decode(str);
        try {
            decoded = new String(bytes,"utf-8");
        }catch(UnsupportedEncodingException e){
            logger.warn("不支持的编码格式",e);
        }
        return decoded;
    }

    public static String encodeURL(String url) {
    	String encoded = null;
		try {
			encoded =  URLEncoder.encode(url, "utf-8");
		} catch (UnsupportedEncodingException e) {
			logger.warn("URLEncode失败", e);
		}
		return encoded;
	}


	public static String decodeURL(String url) {
    	String decoded = null;
		try {
			decoded = URLDecoder.decode(url, "utf-8");
		} catch (UnsupportedEncodingException e) {
			logger.warn("URLDecode失败", e);
		}
		return decoded;
	}

    public static void main(String [] args){
        String str = "abcd{'a':'b'}";
        String encoded = EncryptUtil.encodeUTF8StringBase64(str);
        String decoded = EncryptUtil.decodeUTF8StringBase64(encoded);
        System.out.println(str);
        System.out.println(encoded);
        System.out.println(decoded);

        String url = "== wo";
        String urlEncoded = EncryptUtil.encodeURL(url);
        String urlDecoded = EncryptUtil.decodeURL(urlEncoded);
        
        System.out.println(url);
        System.out.println(urlEncoded);
        System.out.println(urlDecoded);
    }


}
package org.dong.oauth.order.pojo;

import lombok.Data;

/**
 * 用户信息
 */
@Data
public class UserDTO {

    private Integer id;
    private String username;//账号
    private String password;
    private String realName;//用户名
    private String mobile;

}

测试

运行eureak,getway,order,uaa

通过网关进行访问uaa

http://localhost:9003/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=password&redirect_uri=http://www.baidu.com&username=dong&password=123456

getwayUaa

结果

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzMSJdLCJ1c2VyX25hbWUiOiJkb25nIiwic2NvcGUiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiIsIlJPTEVfQVBJIl0sImV4cCI6MTYyMTA3NjU3MSwiYXV0aG9yaXRpZXMiOlsicDEiLCJwMiJdLCJqdGkiOiIwYjQxNDcxNC02NmI2LTRlN2EtOWJiYS1kMzAyYjc0MDc5ZTEiLCJjbGllbnRfaWQiOiJjMSJ9.jnQool3sWTDoogtaAS-K3ioR9JZkx_VamuiRDz7odK4",
    "token_type": "bearer",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzMSJdLCJ1c2VyX25hbWUiOiJkb25nIiwic2NvcGUiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiIsIlJPTEVfQVBJIl0sImF0aSI6IjBiNDE0NzE0LTY2YjYtNGU3YS05YmJhLWQzMDJiNzQwNzllMSIsImV4cCI6MTYyMTMyODU3MSwiYXV0aG9yaXRpZXMiOlsicDEiLCJwMiJdLCJqdGkiOiIyMTM2NTE3MC1iZDFiLTQzNjItOTliNS01ZGY5NDdmYWViZTAiLCJjbGllbnRfaWQiOiJjMSJ9.ji6wkd05HfO619sBB79sNI0cvKWatR3X3SYP2ILO5jM",
    "expires_in": 7199,
    "scope": "ROLE_ADMIN ROLE_USER ROLE_API",
    "jti": "0b414714-66b6-4e7a-9bba-d302b74079e1"
}

我们可以校验一下token信息 post方式

http://localhost:9001/uaa/oauth/check_token
{
    "aud": [
        "res1"
    ],
    "user_name": "dong",
    "scope": [
        "ROLE_ADMIN",
        "ROLE_USER",
        "ROLE_API"
    ],
    "exp": 1621076571,
    "authorities": [
        "p1",
        "p2"
    ],
    "jti": "0b414714-66b6-4e7a-9bba-d302b74079e1",
    "client_id": "c1"
}

通过网关去访问资源服务 (访问r3没有权限)

http://localhost:9003/order/r1

getwayOrder

dong访问资源1

改进

上面我们通过token只获取到了用户名,原因是在网关的AuthFilter类中

 if(userAuthentication != null){
            jsonToken.put("principal",userAuthentication.getName());
            jsonToken.put("authorities",authorities);
        }

一个简单的改法,就是拥有principal存一个用户的所有信息(具体步骤:略)

//取出用户身份信息 
String principal = userJson.getString("principal"); 
//将json转成对象
UserDTO userDTO = JSON.parseObject(principal, UserDTO.class);

参考教程:黑马

完整代码:https://gitee.com/dongjixue/spring-security-and-oauth2.0

posted @ 2021-05-15 17:27  懒鑫人  阅读(594)  评论(0)    收藏  举报