当前位置:   article > 正文

SpringCloud gateway +oauht2.0 password模式+jwt 实现微服务的认证和授权(二)_springcloud整合 oauth2+gateway+jwt+nacos 实现授权码模式的服务认

springcloud整合 oauth2+gateway+jwt+nacos 实现授权码模式的服务认证

1.我们把三个服务分别注册到nacos

1.1:pom文件中加入 nacos依赖(三个服务都得加)

<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
  • 1
  • 2
  • 3
  • 4

1.2:在application.yml中配置nacos连接地址

cloud:
  nacos:
    discovery:
      #指定nacos server的地址
      server-addr: 127.0.0.1:8848
  • 1
  • 2
  • 3
  • 4
  • 5

2.配置gateway

2.1:在application.yml中配置 (注意配置顺序,顺序不对 gateway可能启动不起来)

server:
  port: 9090
spring:
  application:
    name: springcloud-oauth-gateway
  #将服务器注册到nacos中
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    ##是否与服务发现组件进行结合,通过 serviceId 转发到具体的服务实例。默认为false,设为true便开启通过服务中心的自动根据 serviceId 创建路由的功能。
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: springcloud-oauth-auth
          uri: lb://springcloud-oauth-auth
          predicates:
            - Path=/auth/**
        - id: springcloud-oauth-resource
          uri: lb://springcloud-oauth-resource
          predicates:
            - Path=/resource/**
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    password: zhangxiaoyun123
    url: jdbc:mysql://192.168.3.37/zxy_system_service?useUnicode=true&characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull
    username: root
  ##这个的意思 是 在spring cloud服务太多的情况下 bean名字重名的情况下多 设置为true 后重名的bean覆盖以前的bean
  main:
    allow-bean-definition-overriding: true
ribbon:
  MaxAutoRetries: 2 #最大重试次数,当nacos中可以找到服务,但是服务连不上时将会重试,如果nacos中找不到服务则直接走断路器
  MaxAutoRetriesNextServer: 3 #切换实例的重试次数
  OkToRetryOnAllOperations: false  #对所有操作请求都进行重试,如果是get则可以,如果是post,put等操作没有实现幂等的情况下是很危险的,所以设置为false
  ConnectTimeout: 5000  #请求连接的超时时间
  ReadTimeout: 6000 #请求处理的超时时间

##调用方法的超时时间 在调用方配置,被该调用方的所有方法的超时时间都是该值,优先级低于下边的指定配置
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeout-in-milliseconds: 6000


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

3.封装获取token的方法

3.1:创建TokenService

import com.example.springcloudalibabaoauthauth.util.AuthToken;

/**
 * 2 * @Author: ZhangShuai
 * 3 * @Date: 2020/6/18 14:44
 * 4
 */
public interface TokenService {


    /**
     * 根据用户名密码获取令牌
     *
     * @param username 用户名
     * @param password 用户密码
     * @return
     */
    AuthToken passwordVerifyToken(String username, String password);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

3.2:实现TokenService
注(这里将获取到的token存储到redis中,当访问网关的时候,去判断hadler中有没有token,判断这个token是否使用,可用才放行token)

import com.example.springcloudalibabaoauthauth.config.RedisMethod;
import com.example.springcloudalibabaoauthauth.service.TokenService;
import com.example.springcloudalibabaoauthauth.util.AuthToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.net.URI;
import java.util.Map;

/**
 * 2 * @Author: ZhangShuai
 * 3 * @Date: 2020/6/18 14:47
 * 4
 */
@Service
public class TokenServiceImpl implements TokenService {
    /** 这里指的是客户端的信息和密钥 具体看数据库表 oauth_client_details **/
    private final String clientId="client";
    private final String clientSecret="admin";

    //在redis里面的过期时间 3600秒
    private final Long ExpireTime=3600l;

    @Autowired
    LoadBalancerClient loadBalancerClient;

    @Resource(name = "facePlusRestTemplate")
    RestTemplate restTemplate;

    @Autowired
    RedisMethod redisMethod;

    @Override
    public AuthToken passwordVerifyToken(String username, String password) {
        //拿着username 和password 去请求 /auth/oauth/token 来获取到token
        AuthToken token = this.passwordApplyToken(username, password);
        //如果错误信息 不为空的话  走 redis
        if (token.getError() == null && token.getError_description() == null) {
            //把token存储到redis当中
            redisMethod.setStringTime(token.getAccess_token(), token.toString(), ExpireTime);
        }
        return token;
    }

    private AuthToken passwordApplyToken(String username, String password) {
        //从nacos中获取认证服务的实例地址(因为spring security在认证服务中) 服务的名称
        ServiceInstance serviceInstance = loadBalancerClient.choose("springcloud-oauth-auth");
        //此地址就是http://ip:portx
        URI uri = serviceInstance.getUri();
        //令牌申请的地址 http://localhost:8086/auth/oauth/token
        String authUrl = uri + "/auth/oauth/token";
        //定义header
        LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<>();
        String httpBasic = getHttpBasic();
        header.add("Authorization", httpBasic);
        //定义body
        LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "password");
        body.add("username", username);
        body.add("password", password);
        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, header);
        //调用登录认证服务 生成jwt令牌
        ResponseEntity<Map> exchange = restTemplate.exchange(authUrl, HttpMethod.POST, httpEntity, Map.class);
        //申请令牌信息
        AuthToken authToken = makeAuthToken(exchange);
        return authToken;
    }

    /**
     * 设置token值
     *
     * @param exchange 远程调用结果
     * @return
     */
    private AuthToken makeAuthToken(ResponseEntity<Map> exchange) {
        Map bodyMap = exchange.getBody();
        AuthToken authToken = new AuthToken();
        if (bodyMap == null ||
                bodyMap.get("access_token") == null ||
                bodyMap.get("refresh_token") == null ||
                bodyMap.get("jti") == null ||
                bodyMap.get("expires_in") == null
        ) {
            authToken.setError((String) bodyMap.get("error"));
            authToken.setError_description((String) bodyMap.get("error_description"));
            return authToken;
        }
        authToken.setAccess_token((String) bodyMap.get("access_token"));//用户身份令牌
        authToken.setRefresh_token((String) bodyMap.get("refresh_token"));//刷新令牌
        authToken.setJwt_token((String) bodyMap.get("jti"));//jwt令牌
        return authToken;
    }

    /*获取httpBasic的串*/
    private String getHttpBasic() {
        String string = clientId + ":" + clientSecret;
        //将串进行base64编码
        byte[] encode = Base64Utils.encode(string.getBytes());
        return "Basic " + new String(encode);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112

3.3:具体返回实体类可查看代码

4.配置gateway网关拦截器

4.1 GateWayGloFilter:网关拦截器,所有的请求 都会请求网关,网关来进行转发请求

import com.alibaba.fastjson.JSON;
import com.example.springcloudalibabaoatuhgatewat.config.RedisMethod;
import com.example.springcloudalibabaoatuhgatewat.entity.UserJwtVo;
import com.example.springcloudalibabaoatuhgatewat.service.AuthService;
import com.example.springcloudalibabaoatuhgatewat.util.AuthToken;
import com.example.springcloudalibabaoatuhgatewat.util.JwtUtils;
import com.example.springcloudalibabaoatuhgatewat.util.ResponseCodeEnum;
import com.example.springcloudalibabaoatuhgatewat.util.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * 2 * @Author: ZhangShuai
 * 3 * @Date: 2020/6/18 10:11
 * 4
 */
@Slf4j
@Component
public class GateWayGloFilter implements GlobalFilter , Ordered {

    public static final String Authorization = "Authorization";

    //在redis里面的过期时间 3600秒
    private final Long ExpireTime=3600l;

    /*不需要身份验证的路径  */
    public static final String NO_AUTH_PATH = "/auth/password/login,/auth/oauth/check_token,/auth/oauth/token";


    @Autowired
    private AuthService authService;

    @Autowired
    RedisMethod redisMethod;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //获取response对象
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        //获取request对象
        ServerHttpRequest request = exchange.getRequest();
        //没有登录 判断是否是白名单请求
        if (isAllowRequesr(request)) {
            //如果是 放行
            return chain.filter(exchange);
        }
        try {
            //获取header中的token
            List<String> tokenlist = request.getHeaders().get("token");
            if (CollectionUtils.isEmpty(tokenlist)) {
                //没有token,拦截请求
                return getVoidMono(serverHttpResponse, ResponseCodeEnum.TOKEN_MISSION);
            }
            String token = tokenlist.get(0);
            //如果当前token 不在 redis里面 那就证明当前 token是假的 token
            //如果是假的token 那么 请你去 认证服务器 认证去
            String Token = redisMethod.getString(token);
            if (StringUtils.isEmpty(Token)) {
                //把异常返回过去 告诉 他
                return getVoidMono(serverHttpResponse, ResponseCodeEnum.TOKEN_INVALID);
            }
            //如果当你 redis里面的时间 低于了多少秒之后 我去刷新你的token
            long time = redisMethod.getTime(token);
            //当用户登录超过三小时就重新刷新令牌
            if (time < 50) {
                //刷新token
                List<String> refreshTokenlist = request.getHeaders().get("refreshToken");
                String refreshToken = refreshTokenlist.get(0);
                //调用服务重新生成令牌
                AuthToken authToken = authService.refresh_token(refreshToken);
                if (!org.springframework.util.StringUtils.isEmpty(authToken)) {
                    String jsonString = JSON.toJSONString(authToken);
                    //删除Redis原有的令牌 并存入新的令牌
                    if (redisMethod.delString(token)) {
                        redisMethod.setStringTime(authToken.getAccess_token(), jsonString, ExpireTime);
                        //将令牌响应给前端
                        return returnsToken(serverHttpResponse, authToken);
                    }
                }
            }
            //去认证服务器登录
            //通过jwt对token进行解析获取用户信息
            UserJwtVo userJwtFromHeader = JwtUtils.getUserJwtFromToken(token);
            log.info("用户{}正在访问资源:{}", userJwtFromHeader.getName(), request.getPath());
            //权限判断 校验通过,请求头增强,放行
//            Map map = JwtUtils.parsingJwt(token);
//            String authorities = map.get("authorities").toString();
//            System.out.println(authorities);
//            if (!authorities.contains(path)) {
//                return getVoidMono(serverHttpResponse, ResponseCodeEnum.REFRESH_TOKEN_QUANXIANNOT);
//            }
            //增强请求头
            request.mutate().header(Authorization, "Bearer " + token, "token", token);
            // System.out.println(request.getHeaders().toString());
            //授权下面的服务 并且放行
            //放行
            return chain.filter(exchange);

        } catch (Exception e) {
            log.info("服务解析用户信息失败:", e);
            //内部异常 返回500
            exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            return exchange.getResponse().setComplete();
        }
    }

    /**
     * 创建response返回提示信息
     *
     * @param serverHttpResponse
     * @param responseCodeEnum
     * @return
     */
    private Mono<Void> getVoidMono(ServerHttpResponse serverHttpResponse, ResponseCodeEnum responseCodeEnum) {
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        ResponseResult responseResult = ResponseResult.error(responseCodeEnum.getCode(), responseCodeEnum.getMessage());
        DataBuffer dataBuffer = serverHttpResponse.bufferFactory().wrap(JSON.toJSONString(responseResult).getBytes());
        return serverHttpResponse.writeWith(Flux.just(dataBuffer));
    }

    /**
     * 创建response返回刷新令牌
     *
     * @param serverHttpResponse
     * @param o
     * @return
     */
    private Mono<Void> returnsToken(ServerHttpResponse serverHttpResponse, Object o) {
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        DataBuffer dataBuffer = serverHttpResponse.bufferFactory().wrap(JSON.toJSONString(o).getBytes());
        return serverHttpResponse.writeWith(Flux.just(dataBuffer));
    }

    /**
     * 判断请求是否在白名单
     *
     * @param request
     * @return
     */
    private boolean isAllowRequesr(ServerHttpRequest request) {
        //获取当前请求的path和method
        String path = request.getPath().toString();
        String method = request.getMethodValue();
        //判断是否允许
        if (StringUtils.startsWith(NO_AUTH_PATH, path)) {
            //是许可的路径 放行
            return true;
        }
        //不是白名单请求
        return false;
    }

    /*设置当前类在spring中的加载顺序*/
    @Override
    public int getOrder() {
        return -2;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174

4.2 AuthService:当redis中token快过期的时候,拿着access_token去刷新token的类

import com.example.springcloudalibabaoatuhgatewat.util.AuthToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.net.URI;
import java.util.Map;

@Slf4j
@Service
public class AuthService {

    /** 这里指的是客户端的信息和密钥 具体看数据库表 oauth_client_details **/
    private final String clientId="client";
    private final String clientSecret="admin";

    @Autowired
    LoadBalancerClient loadBalancerClient;

    @Resource(name = "facePlusRestTemplate")
    RestTemplate restTemplate;

    /**
     * 调用服务 获取刷新令牌
     *
     * @param token
     * @return
     */
    public AuthToken refresh_token(String token) {
        //从nacos中获取认证服务的实例地址(因为spring security在认证服务中) 服务的名称
        ServiceInstance serviceInstance = loadBalancerClient.choose("springcloud-oauth-auth");
        //此地址就是http://ip:portx
        URI uri = serviceInstance.getUri();
        //令牌校验的地址 http://localhost:8086/auth/oauth/check_token
        String authUrl = uri + "/auth/oauth/token";

        //定义header
        LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<>();
        String httpBasic = getHttpBasic(clientId, clientSecret);
        header.add("Authorization", httpBasic);

        //定义body
        LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "refresh_token");
        body.add("refresh_token", token);

        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, header);
        //调用登录认证服务 刷新jwt令牌
        ResponseEntity<Map> exchange = restTemplate.exchange(authUrl, HttpMethod.POST, httpEntity, Map.class);
        //申请令牌信息
        AuthToken authToken = makeAuthToken(exchange);
        return authToken;
    }

    private AuthToken makeAuthToken(ResponseEntity<Map> exchange) {
        Map bodyMap = exchange.getBody();
        AuthToken authToken = new AuthToken();
        if (bodyMap == null ||
                bodyMap.get("access_token") == null ||
                bodyMap.get("refresh_token") == null ||
                bodyMap.get("jti") == null ||
                bodyMap.get("expires_in") == null
        ) {
            authToken.setError((String) bodyMap.get("error"));
            authToken.setError_description((String) bodyMap.get("error_description"));
            return authToken;
        }
        authToken.setAccess_token((String) bodyMap.get("access_token"));//用户身份令牌
        authToken.setRefresh_token((String) bodyMap.get("refresh_token"));//刷新令牌
        authToken.setJwt_token((String) bodyMap.get("jti"));//jwt令牌
        return authToken;
    }

    //获取httpbasic的串
    private String getHttpBasic(String clientId, String clientSecret) {
        String string = clientId + ":" + clientSecret;
        //将串进行base64编码
        byte[] encode = Base64Utils.encode(string.getBytes());
        return "Basic " + new String(encode);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

5.资源服务器拿到用户的登录信息

5.1 在 第二章节的时候 我们配置了 CustomUserInformationConverter 自定义jwt信息这个类 ,所以我们在资源服务器可以拿到这个类定义的东西 ,配置资源服务器 jwt解析的类

5.2 CustomUserInformationConverter :解析jwt内容类

import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.stereotype.Component;

import java.util.Map;


/**
 * 2 * @Author: ZhangShuai
 * 3 * @Date: 2020/6/12 17:03
 * 4  资源服务器获取 认证服务器的自定义内容
 */
@Component
public class CustomUserInformationConverter extends DefaultAccessTokenConverter {


    @Override
    public OAuth2Authentication extractAuthentication(Map<String, ?> claims) {
        OAuth2Authentication authentication
                = super.extractAuthentication(claims);
        authentication.setDetails(claims);
        return authentication;
    }


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

5.3 JwtTokenStoreConfig:设置jwt的解析类

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;

import javax.annotation.Resource;


/**
 * 2 * @Author: ZhangShuai
 * 3 * @Date: 2020/6/12 17:03
 * 4  token使用jwt的配置
 */
@Configuration
public class JwtTokenStoreConfig {

    public static final String EncryptedFile = "xmzs-jwt.jks";

    public static final String Secret = "557554";

    public static final String EncryptedFileName = "557554";

    @Resource
    private CustomUserInformationConverter customUserInformation;

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

    //使用非对称加密算法
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(CustomUserInformationConverter customUserInformationApadter) {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(EncryptedFileName);
        converter.setAccessTokenConverter(customUserInformationApadter);
//        //找到 加密文件所在的地址 读取classpath里面的 xm-jwt.jks文件
//        ClassPathResource pathResource = new ClassPathResource(EncryptedFile);
//        //秘钥配置时的密码
//        KeyStoreKeyFactory keyFactory = new KeyStoreKeyFactory(pathResource, Secret.toCharArray());
//        //使用keystore得到秘钥
//        KeyPair pair = keyFactory.getKeyPair(EncryptedFileName);
//        //设置秘钥
//        converter.setKeyPair(pair);
        //DefaultAccessTokenConverter accessTokenConverter = (DefaultAccessTokenConverter) converter.getAccessTokenConverter();
        //accessTokenConverter.setUserTokenConverter(customUserInformationApadter);
        return converter;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

5.4 ResourceServerConfig :在资源服务器配置该token解析类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;


/**
 * 2 * @Author: ZhangShuai
 * 3 * @Date: 2020/6/18 11:52
 * 4 这个注解表示 当前是一个资源服务器
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {


    @Autowired
    JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    TokenStore tokenStore;

    /**
     * 自定义 资源服务器 权限认证失败的异常
     */
    @Bean
    public AccessDeniedHandler getAccessDeniedHandler() {
        return new ResourceAccessDeniedHandler();
    }

    /**
     * 自定义 资源服务器 未经授权异常
     *
     * @return
     */
    @Bean
    public AuthenticationEntryPoint getAuthenticationEntryPoint() {
        return new ResourceAuthenticationEntryPoint();
    }

    /**
     * 设置资源服务器的 token 和自定义的异常
     *
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenStore(tokenStore)
                .accessDeniedHandler(getAccessDeniedHandler())
                .authenticationEntryPoint(getAuthenticationEntryPoint());
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

5.5 Subject :配置当前登录人的信息 (这里面是自定义的)

import lombok.Data;

/**
 * 2 * @Author: ZhangShuai
 * 3 * @Date: 2020/6/19 14:28
 * 4
 */
@Data
public class Subject {

    /**
     * 用户id
     */
    private Long id;

    /**
     * 用户名
     */
    private String username;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

5.6 GetSubject:获取jwt里面自定义的内容 并且赋值给 Subject类 (这里面是自定义的)

import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.stereotype.Component;

import java.util.LinkedHashMap;

/**
 * 2 * @Author: ZhangShuai
 * 3 * @Date: 2020/6/19 14:30
 * 4   获取当前用户的信息
 */
@Component
public class GetSubject {


    public Subject getSubject(OAuth2Authentication oAuth2Authentication) {
        OAuth2AuthenticationDetails oAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) oAuth2Authentication.getDetails();
        LinkedHashMap details = (LinkedHashMap) oAuth2AuthenticationDetails.getDecodedDetails();
        Subject subject=new Subject();
        //获取自定义的id
        subject.setId(Long.parseLong(details.get("id").toString()));
        //
        subject.setUsername(String.valueOf(details.get("username")));
        return subject;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

5.7 获取当前用户的信息
在这里插入图片描述

import com.example.springcloudalibabaoauthresource.util.GetSubject;
import com.example.springcloudalibabaoauthresource.util.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 2 * @Author: ZhangShuai
 * 3 * @Date: 2020/6/15 8:47
 * 4
 */
@RestController
public class HelloController {

    @Autowired
    GetSubject getSubject;

    @GetMapping("/hello")
    public String hello(OAuth2Authentication oAuth2Authentication) {
        Subject subject = getSubject.getSubject(oAuth2Authentication);
        System.out.println("subject = " + subject);
        return "Hello";
    }


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

6.认证和授权的演示

6.1 通过网关去获取token
在这里插入图片描述
6.2 带着token 访问资源服务器 拿到当前登录人的信息
在这里插入图片描述
在这里插入图片描述
大功告成:项目源码 GitHub直达

注(请修改 jdbc的连接 和 redis的连接 )

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/84464
推荐阅读
相关标签
  

闽ICP备14008679号