其他分享
首页 > 其他分享> > 11-单点登录系统微服务版实践

11-单点登录系统微服务版实践

作者:互联网

文章目录

创建聚合工程

项目架构

在这里插入图片描述

工程结构

在这里插入图片描述

创建工程

第一步:创建parent工程,例如:
在这里插入图片描述
第二步:创建jt-sso-auth工程
在这里插入图片描述

第三步:创建jt-sso-resource工程
在这里插入图片描述

第四步:创建jt-sso-gateway工程

在这里插入图片描述

第五步:创建jt-sso-ui工程

在这里插入图片描述

配置项目工程

jt-cloud-sso

打开工程pom文件,添加依赖配置,代码如下:

   <dependencyManagement>
        <!--Spring Framework与SpringBoot的关系
        Spring框架是资源整合框架,基于IOC思想进行资源整合.SpringBoot基于Spring框架,
        用于简化Spring框架整合资源的工程,同时为微服务工程创建和配置提供了更加便利条件
        -->
        <dependencies>
            <!--Spring Boot依赖(spring-boot-starter-parent)
            思考:05-jt-sca工程中依赖的添加方式-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.3.2.RELEASE</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
            <!--Spring Cloud 依赖(定义了微服务规范)-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR8</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
            <!--Spring Cloud Alibaba依赖(基于spring微服务规范做了具体落地实现)-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.5.RELEASE</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>

   
   
  • 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

jt-sso-common

打开工程pom文件,添加依赖配置,代码如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <!--provided 表示这个依赖只在编译阶段有效-->
        <scope>provided</scope>
    </dependency>
</dependencies>

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

jt-sso-auth

pom.xml

 <dependencies>
        <!--spring web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--spring security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- jwt -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>
        <!--spring cloud alibaba nacos discovery -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--spring cloud alibaba nacos config -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!--jt-common-->
        <dependency>
            <groupId>com.jt</groupId>
            <artifactId>jt-sso-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependencies</span><span class="token punctuation">&gt;</span></span>

bootstrap.yml

在resource目录下创建bootstrap.yml配置文件,代码如下:

server:
  port: 8081
spring:
  application:
    name: jt-sso-auth
  datasource:
    url: jdbc:mysql:///jt_security?serverTimezone=Asia/Shanghai&characterEncoding=utf8
    username: root
    password: root
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848
        file-extension: yml
logging:
  level:
    com.jt: debug

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

启动类

package com.jt.sso;
@SpringBootApplication
public class AuthApplication {
    public static void main(String[] args) {
        SpringApplication.run(AuthApp.class,args);
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

jt-sso-resource

pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>com.jt</groupId>
            <artifactId>jt-sso-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

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

bootstrap.yml

server:
  port: 8090
spring:
  application:
    name: jt-sso-resource
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848
        file-extension: yml
logging:
  level:
    com.jt: debug

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

启动类

package com.jt;
@EnableGlobalMethodSecurity(prePostEnabled = true)
@SpringBootApplication
public class ResourceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ResourceApplication.class,args);
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

jt-sso-gateway

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
</dependencies>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

bootstrap.yml

server:
  port: 9000
spring:
  application:
    name: jt-sso-gateway
  cloud:
    nacos:
      discovery: #服务发现
        server-addr: localhost:8848
      config: #服务配置
        server-addr: localhost:8848
        file-extension: yml
    gateway:
      discovery:
        locator:
          enabled: true  #开启基于服务名查找服务实例
      routes:
        - id: router01
          uri: lb://jt-sso-auth #jt-sso-auth 服务名
          predicates:
            - Path=/auth/login
          filters:
            - StripPrefix=1  #去掉请求path中的第一层目录

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

启动类

package com.jt;
@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class,args);
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

jt-sso- ui

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

application.yml

server:
  port: 80

 
 
  • 1
  • 2

启动类

package com.jt;

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

工程业务代码实现

jt-sso-common

代码结构

在这里插入图片描述
说明:途中代码可从前面写过的单点登陆系统去拷贝。

问题分析

这个工程中添加的web依赖,为什么添加scope元素的值为provided.

jt-sso-auth

代码结构

在这里插入图片描述
说明:途中代码可从前面写过的单点登陆系统去拷贝。

AuthController

创建AuthController对象用于对外提供令牌的解析任务,代码如下:

package com.jt.sso.controller;
@RestController
public class AuthController {
    @GetMapping("/auth/info")
    public Map<String,Object> getAuthentication(String token){
        System.out.println("token==="+token);
        Claims claims=JwtUtils.getClaimsFromToken(token);
        boolean flag=claims.getExpiration().before(new Date());
        String username=(String)claims.get("username");
        List<String> list=(List<String>)
                claims.get("authorities");
        Map<String,Object> map=new HashMap<>();
        map.put("expired",flag);
        map.put("username",username);
        map.put("authorities",list);
        return map;
    }
}

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

定义完这个controller以后,需要修改SpringConfig类中的configure(HttpSecurity http)方法, 对解析令牌的/auth/info这个路径进行放行,例如

...
http.authorizeRequests()
            .antMatchers("/auth/info")
            .permitAll()
            .anyRequest()//所有请求->对应任意资源
            .authenticated();//必须认证

问题分析

在这里插入图片描述

在这里插入图片描述

jt-sso-resource

代码结构

在这里插入图片描述

RestTemplate服务调用

第一步:在启动类中,初始化一个RestTemplate对象,例如:

@Bean
@LoadBalanced
public RestTemplate restTemplate(){
  return new RestTemplate();
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5

第二步:在TokenInterceptor中添加如下代码,初始化RestTemplate对象

private RestTemplate restTemplate;
public TokenInterceptor(RestTemplate restTemplate) {
    this.restTemplate = restTemplate;
}

 
 
  • 1
  • 2
  • 3
  • 4

第三步:修改preHandle方法,基于RestTemplate进行远程服务调用,代码如下:

@Override
public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        //1.从请求中获取token对象(如何获取取决于你传递token的方式:header,params)
        String token=request.getHeader("token");
        //2.验证token是否存在
        if(token==null||"".equals(token))
            throw new RuntimeException("请先登录");//WebUtils.writeJsonToClient
        //3.验证token是否过期,解析token中的认证和权限信息(一般存储在jwt格式中的负载部分)
       //基于restTemplate进行远程服务调用
        String url="http://jt-sso-auth/auth/info?token="+token;
        Map<String,Object> map= restTemplate.getForObject(url,Map.class);
        Boolean expired=(Boolean)map.get("expired");
        if(expired)throw new RuntimeException("登录超时");
        String username=(String)map.get("username");
        List<String> list=(List<String>)map.get("authorities");
        //4.封装和存储认证和权限信息
        //4.1构建UserDetail对象(用户身份的象征-类似于一张名片,微信的二维码)
        UserDetails userDetails=User.builder()
                .username(username)
                .password("")
                .authorities(list.toArray(new String[]{}))
                .build();
        //4.2构建Security权限交互对象(记住,固定写法)
        PreAuthenticatedAuthenticationToken authToken=
                new PreAuthenticatedAuthenticationToken(
                        userDetails,//用户身份
                        userDetails.getPassword(),
                        userDetails.getAuthorities());
        //4.3将权限交互对象与当前请求进行绑定
        authToken.setDetails(new WebAuthenticationDetails(request));
        //5.4.将认证后的token存储到Security上下文(会话对象)
        SecurityContextHolder.getContext().setAuthentication(authToken);
        return true;
    }

 
 
  • 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

第四步:在拦截器的配置类中,修改如下代码:

  @Autowired
  private RestTemplate restTemplate;
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TokenInterceptor(restTemplate))
                 //配置要拦截的url
                .addPathPatterns("/**");//doCreate,doUpdate,doDelete
  }

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

第五步:启动工程基于postman进行访问测试

Feign方式服务调用

第一步:添加Feign依赖

org.springframework.cloud
spring-cloud-starter-openfeign

第二步:在启动类上添加启动Feign应用注解

@EnableFeignClients  

 
 
  • 1

第三步:定义feign远程调用接口

package com.jt.res.service;

@FeignClient(name = “jt-sso-auth”,contextId = “remoteAuthService”)
public interface RemoteAuthService {
//@GetMapping中的地址为jt-sso-auth服务中的一个地址
@GetMapping("/auth/info")
public Map<String,Object> getAuthentication(
@RequestParam(“token”) String token);
}

第四步:在TokenInterceptor中定义RemoteAuthService属性及构造方法,代码如下:

  private RemoteAuthService remoteAuthService;

public TokenInterceptor(RemoteAuthService remoteAuthService) {
this.remoteAuthService = remoteAuthService;
}

第五步:修改拦截器的preHandle方法,基于Feign进行远程服务调用,代码如下:

@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.从请求中获取token对象(如何获取取决于你传递token的方式:header,params)
        String token=request.getHeader("token");
        //2.验证token是否存在
        if(token==null||"".equals(token)) throw new RuntimeException("请先登录");//WebUtils.writeJsonToClient
        //3.验证token是否过期,解析token中的认证和权限信息(一般存储在jwt格式中的负载部分)
        //去认证中心获取这些数据?(作业-RestTemplate或者Feign)
       //基于feign方式进行远程服务调用
        Map<String,Object> map=remoteAuthService.getAuthentication(token);
        Boolean expired=(Boolean)map.get("expired");
        if(expired)throw new RuntimeException("登录超时");
        String username=(String)map.get("username");
        List<String> list=(List<String>)map.get("authorities");
    <span class="token comment">//4.封装和存储认证和权限信息</span>
    <span class="token comment">//4.1构建UserDetail对象(用户身份的象征-类似于一张名片,微信的二维码)</span>
    <span class="token class-name">UserDetails</span> userDetails<span class="token operator">=</span><span class="token class-name">User</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">username</span><span class="token punctuation">(</span>username<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">password</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">authorities</span><span class="token punctuation">(</span>list<span class="token punctuation">.</span><span class="token function">toArray</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">//4.2构建Security权限交互对象(记住,固定写法)</span>
    <span class="token class-name">PreAuthenticatedAuthenticationToken</span> authToken<span class="token operator">=</span>
            <span class="token keyword">new</span> <span class="token class-name">PreAuthenticatedAuthenticationToken</span><span class="token punctuation">(</span>
                    userDetails<span class="token punctuation">,</span><span class="token comment">//用户身份</span>
                    userDetails<span class="token punctuation">.</span><span class="token function">getPassword</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                    userDetails<span class="token punctuation">.</span><span class="token function">getAuthorities</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">//4.3将权限交互对象与当前请求进行绑定</span>
    authToken<span class="token punctuation">.</span><span class="token function">setDetails</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">WebAuthenticationDetails</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">//5.4.将认证后的token存储到Security上下文(会话对象)</span>
    <span class="token class-name">SecurityContextHolder</span><span class="token punctuation">.</span><span class="token function">getContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setAuthentication</span><span class="token punctuation">(</span>authToken<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

第六步:在拦截器的配置类中,修改如下代码:

 @Autowired
 private RemoteAuthService remoteAuthService;
@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new TokenInterceptor(remoteAuthService))
             //配置要拦截的url
             .addPathPatterns("/**");//doCreate,doUpdate,doDelete
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

第七步:启动工程基于postman进行访问测试

问题分析

jt-sso-gateway

跨域设计

当客户端基于ajax访问服务端资源时,因为跨域问题不能直接访问,我们可以在服务端通过过滤器,打开跨域访问。例如:


package com.jt.gateway.config;

@Configuration
public class CorsFilterConfig {
@Bean
public CorsWebFilter corsFilter(){
System.out.println(“corsWebFilter()”);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
CorsConfiguration corsConfiguration = new CorsConfiguration();
//1.配置跨域
//允许哪种请求头跨域
corsConfiguration.addAllowedHeader("");
//允许哪种方法类型跨域 get post delete put
corsConfiguration.addAllowedMethod("");
// 允许哪些请求源跨域
corsConfiguration.addAllowedOrigin("*");
// 是否携带cookie跨域
corsConfiguration.setAllowCredentials(true);
//允许跨域的路径
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
}

第六步:在拦截器的配置类中,修改如下代码:

jt-sso- ui

代码结构

在这里插入图片描述
将以前做单点登录系统时,ui工程中的static目录拷贝到当前项目,然后修改login.html,index.html中访问网关的url,最后对static目录进行rebuild,然后启动项目进行访问测试.

总结(summary)

**加粗样式**

标签:11,单点,登录,jt,spring,token,sso,public,cloud
来源: https://blog.csdn.net/m0_56858230/article/details/119908637