SpringSecurity在微服务中的应用

8/1/2022 安全框架

# 前述

# SpringCloud几个依赖关系

依赖 版本号
spring-boot 2.7.2
spring-cloud 2021.0.3
spring-cloud-alibaba 2021.0.1.0
nacos-client 2.1.0
openfeign 3.1.3

# 目录结构

SpringSecurity

# 父POM

<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">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>security-cloud</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <name>security-cloud</name>
  <description>security-cloud:security微服务下的使用</description>

  <properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-boot.version>2.7.2</spring-boot.version>
    <spring-cloud.version>2021.0.3</spring-cloud.version>
    <spring-cloud-alibaba.version>2021.0.1.0</spring-cloud-alibaba.version>
    <alibaba.nacos.version>2.1.0</alibaba.nacos.version>
    <cloud-openfeign.version>3.1.3</cloud-openfeign.version>
    <mybatis-plus-boot-starter.version>3.5.1</mybatis-plus-boot-starter.version>
    <druid-spring-boot-starter.version>1.2.8</druid-spring-boot-starter.version>
    <jwt.version>0.9.1</jwt.version>
    <hutool-all.version>5.8.4</hutool-all.version>
    <commons-lang3.version>3.12.0</commons-lang3.version>
    <fastjson2.version>2.0.9</fastjson2.version>
  </properties>

  <dependencyManagement>
    <dependencies>

      <!-- SpringBoot 依赖配置 -->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>${spring-boot.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

      <!-- SpringCloud 微服务 -->
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

      <!--spring-cloud-alibaba-->
      <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>${spring-cloud-alibaba.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

      <!-- Alibaba Nacos 配置 -->
      <dependency>
        <groupId>com.alibaba.nacos</groupId>
        <artifactId>nacos-client</artifactId>
        <version>${alibaba.nacos.version}</version>
      </dependency>

      <!-- openfeign -->
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
        <version>${cloud-openfeign.version}</version>
      </dependency>

      <!--mybatis-plus 持久层-->
      <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>${mybatis-plus-boot-starter.version}</version>
      </dependency>

      <!-- JWT -->
      <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>${jwt.version}</version>
      </dependency>

      <!--JSON解析-->
      <dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2</artifactId>
        <version>${fastjson2.version}</version>
      </dependency>

      <!--java工具类-->
      <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>${hutool-all.version}</version>
      </dependency>

      <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>${commons-lang3.version}</version>
      </dependency>

    </dependencies>
  </dependencyManagement>

  <dependencies>
    <!-- bootstrap 启动器:解决Cloud高版本下不先加载bootstrap.properties问题
        see github issue:https://github.com/alibaba/spring-cloud-alibaba/issues/1994
     -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-bootstrap</artifactId>
    </dependency>
  </dependencies>

  <modules>
    <module>security-common</module>
    <module>security-modules</module>
    <module>security-gateway</module>
  </modules>
  <packaging>pom</packaging>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <source>${java.version}</source>
          <target>${java.version}</target>
          <encoding>${project.build.sourceEncoding}</encoding>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>
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

# security-common-security模块

SpringSecurity

# 退出处理器

/**
 * @version 1.0.0
 * @className: LogoutSuccessHandlerImpl
 * @description: 退出处理器
 * @author: LiJunYi
 * @create: 2022/7/26 8:34
 */
@Configuration
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
{
    @Resource
    private TokenManager tokenManager;

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication e) throws IOException {
        LoginUser loginUser = tokenManager.getLoginUser(request);
        if(ObjectUtil.isNotNull(loginUser))
        {
            //移除
            tokenManager.removeToken(loginUser.getToken());
        }
        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success("退出成功")));
    }
}

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

# security-gateway网关模块

# 依赖引入

注意事项: spring-cloud-starter-alibaba-nacos-config 模块移除了 spring-cloud-starter-bootstrap 依赖,如果你想以旧版的方式使用,你需要手动加上该依赖

详细见:Spring Cloud Alibaba 2021.0.1.0 升级指南 (opens new window)

<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>security-cloud</artifactId>
        <groupId>org.example</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>security-gateway</artifactId>

    <dependencies>
        <!--引入公共模块-->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>security-common-base</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>

        <!--gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <!--nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.7.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
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

# bootstrap.yml

server:
  port: 9527
spring:
  application:
    name: security-gateway
  profiles:
    active: dev
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
      routes:
        - id: security-system
          uri: lb://security-system
          predicates:
            - Path=/sys/**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# security-system业务模块

# 依赖引入

<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>security-modules</artifactId>
        <groupId>org.example</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <artifactId>security-system</artifactId>

    <dependencies>
        <!--引入公共模块-->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>security-common-base</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        <!--引入security模块-->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>security-common-security</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        <!--nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--服务调用-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

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

        <!-- SpringBoot Actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--lombok用来简化实体类:需要安装lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--JSON解析-->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
        </dependency>

        <!--java工具类-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>

    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
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

# application.yml

server:
  port: 8009
spring:
  application:
    name: security-system
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  redis:
    host: localhost
    port: 6379
    database: 0
    password:
    timeout: 10s
    lettuce:
      pool:
        min-idle: 0
        max-idle: 8
        max-active: 8
        max-wait: -1ms
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/security-simple?useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: admin
    driver-class-name: com.mysql.cj.jdbc.Driver
#mybatis-plus
mybatis-plus:
  mapper-locations: classpath:/mapper/**/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
management:
  endpoints:
    web:
      exposure:
        exclude: '*'
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

# MyUserDetailsServiceImpl

/**
 * @version 1.0.0
 * @className: MyUserDetailsServiceImpl
 * @description: security具体登录逻辑
 * @author: LiJunYi
 * @create: 2022/7/26 9:11
 */
@Service
public class MyUserDetailsServiceImpl implements UserDetailsService
{
    private static final Logger log = LoggerFactory.getLogger(MyUserDetailsServiceImpl.class);

    private final ISysUserService userService;

    private final SysPermissionService permissionService;

    /**
     * 构造器注入
     *
     * @param userService       用户服务
     * @param permissionService 许可服务
     */
    public MyUserDetailsServiceImpl(ISysUserService userService, SysPermissionService permissionService)
    {
        this.userService = userService;
        this.permissionService = permissionService;
    }


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
    {
        SysUser user = userService.getByUsername(username);
        if (ObjectUtil.isNull(user))
        {
            log.info("登录用户:{} 不存在.", username);
            throw new ServiceException("登录用户:" + username + " 不存在");
        }
        // 获取角色
        List<SysRole> roles = permissionService.getRolesByUserId(user);
        user.setRoles(roles);
        return createLoginUser(user);
    }

    public UserDetails createLoginUser(SysUser user)
    {
        return new LoginUser(Convert.toLong(user.getId()), 1000L, user, permissionService.getMenuPermission(user));
    }

}
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

# 登录处理类

/**
 * @version 1.0.0
 * @className: SysLoginService
 * @description: 登录业务
 * @author: LiJunYi
 * @create: 2022/7/27 12:53
 */
@Component
public class SysLoginService
{
    @Autowired
    private TokenManager tokenManager;

    @Resource
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private ISysUserService userService;

    /**
     * 登录验证
     *
     * @param username 用户名
     * @param password 密码
     * @return 结果
     */
    public String login(String username, String password)
    {
        // 用户验证
        Authentication authentication;
        try
        {
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(username, password));
        }
        catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {
                throw new ServiceException("用户名密码错误");
            }
            else
            {
                throw new ServiceException(e.getMessage());
            }
        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        // 生成token
        return tokenManager.createToken(loginUser);
    }
}
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

# 启动服务

1、分别启动我们的Redis以及Nacos

2、启动业务模块以及网关模块

3、访问:http://localhost:8848/nacos/,观察Nacos控制台服务列表

SpringSecurity

# 测试效果图

SpringSecurity

SpringSecurity

SpringSecurity

# 最后

目前这个示例只是 SpringSecurity 在微服务中的简单使用,而且鉴权等也没有放在网关上进行,而是放在了业务模块上。

后续会进一步修改,将鉴权放在网关上。然后就是会加入 Spring Security Oauth2 来实现服务间的鉴权。

如果示例中有什么不对的地方,希望大家多多指正,有修改才有进步!