security单点登录案例
案例简介
-
前端发送登录请求,登录成功后,将用户信息及该用户所拥有的权限保存到redis数据库中,同时生成token,将token放到cookie中返回给前端;之后前端每次向后端发送请求时,将token保存在请求头中发送给后端,后端接受到后与redis中的token进行比较,若一致则可以操作
-
数据库设计
-
需求功能
1. 登录认证
2. 创建角色,为角色分配菜单
3. 创建用户
4. 为用户分配角色权限
- 所需技术
GateWay网关:对外暴露一个统一的端口,当访问该服务器端口时,网关会通过Nacos(注册中心)将(反向代理的方式)请求转发到自己的端口
Jwt:用于对数据加密处理成token
Redis
Swagger
- 项目结构
acl_parent 父工程,在pom文件中定义依赖版本
|
|- common 子模块
| |- service_base common模块中的子模块,如md5工具类
| |- spring_security SpringSecurity配置
|
|- infrastructure 子模块
| |- api_gateway 网关配置
|
|- service 子模块
|- service_acl 权限管理
- 按以上结果创建项目,在acl_parent的pom中添加
pom ,同时可看到如下
<modules>
<module>common</module>
<module>infrastructure</module>
<module>service</module>
</modules>
- 在common、infrastructure、service子模块pom中添加如下,同时可看到子模块
<packaging>pom</packaging>
- 在最底层的子模块中不需要添加
pom - 导入所需的依赖
开发步骤
-
公共类、security公共类、security业务类
-
编写公共模块
-
security公共模块
-
security业务类编写
-
启动mysql、redis、nacos,导入sql脚本
-
编写UserDetailsServiceImpl
// security-service模块
import com.chnq.security.entity.User;
import com.chnq.security.service.PermissionService;
import com.chnq.security.service.UserService;
// common-security模块
import com.chnq.security.entity.SecurityUser;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserService userService;
@Autowired
private PermissionService permissionService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名查询数据
User user = userService.selectByUsername(username);
//判断
if(user == null) {
throw new UsernameNotFoundException("用户不存在");
}
// 导入common-security模块中的实体对象
com.chnq.security.entity.User curUser = new com.chnq.security.entity.User();
BeanUtils.copyProperties(user,curUser);
//根据用户查询用户权限列表
List<String> permissionValueList = permissionService.selectPermissionValueByUserId(user.getId());
SecurityUser securityUser = new SecurityUser();
securityUser.setCurrentUserInfo(curUser);
securityUser.setPermissionValueList(permissionValueList);
return securityUser;
}
}
- 业务模块简介
RoleController # 角色
PermissionController # 菜单
UserController # 用户
IndexController # 登录成功后对象返回的对象交给security管理,从security中获取用户信息
编写网关模块
- pom
<?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>mysecurity</artifactId>
<groupId>com.chnq.security</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>api-gateway</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!-- 公共模块中的web starter依赖与gateway冲突 -->
<!-- <dependency>-->
<!-- <groupId>com.chnq.security</groupId>-->
<!-- <version>1.0-SNAPSHOT</version>-->
<!-- <artifactId>common-base</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<!--gson-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<!--服务调用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
</project>
- yml
server:
port: 8222
spring:
application:
name: service-gateway
cloud:
gateway:
discovery:
locator:
enabled: true # 服务发现能找到路由
#路由规则 根据指定规则访问对应路径
routes:
- id: security-service
predicates: Path=/*/acl/**
uri: lb://security-service
nacos:
discovery:
server-addr: 192.168.97.34:8848
- 跨域配置类
package com.chen.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
- 配置网关报错
# 1. java.lang.IllegalStateException: Failed to introspect Class [org.springframework.cloud.gateway.config.GatewayAutoConfiguration$NettyConfiguration] from ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader@7b1d7fff]
# 错误原因:springcloud的版本和springboot的版本对应不上
# 解决方案:将gateway版本改为与spring cloud一致
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
# 2. 启动项目报错spring mvc found on classpath, which is incompatible with spring cloud
# 错误原因:web starter依赖与gateway冲突
# 解决方案:查看父工程pom或导入的公共模块中包含web starter