一.环境准备
创建工程,结构如下:
父工程pom:
<properties>
<java.version>1.8</java.version>
<mybatis-plus.version>3.0.5</mybatis-plus.version>
<velocity.version>2.0</velocity.version>
<swagger.version>2.7.0</swagger.version>
<jwt.version>0.7.0</jwt.version>
<fastjson.version>1.2.28</fastjson.version>
<gson.version>2.8.2</gson.version>
<json.version>20170516</json.version>
<cloud-alibaba.version>0.2.2.RELEASE</cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<!--Spring Cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--mybatis-plus 持久层-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--swagger ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>${json.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
common模块pom:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided </scope>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<scope>provided </scope>
</dependency>
<!--lombok用来简化实体类:需要安装lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided </scope>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<scope>provided </scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<scope>provided </scope>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring2.X集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
common模块下的spring_secutiry模块pom:
<dependencies>
<dependency>
<groupId>com.study</groupId>
<artifactId>service_base</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- Spring Security依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
</dependencies>
service模块pom:
<dependencies>
<dependency>
<groupId>com.study</groupId>
<artifactId>service_base</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--服务注册-->
<dependency>
<groupId>org.springframework.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>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
<!--lombok用来简化实体类:需要安装lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--gson-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
二.编写代码
1.common模块编写
1)service_base模块
@ControllerAdvice //此注解用来实现全局异常处理、全局数据处理、全局数据预处理
@Slf4j
public class GlobalExceptionHandler {
//全局异常类
//指定出现什么异常执行这个方法
@ExceptionHandler(Exception.class)
@ResponseBody //为了返回数据
public R error(Exception e) {
e.printStackTrace();
return R.error().message("执行了全局异常处理..");
}
//特定异常
@ExceptionHandler(ArithmeticException.class)
@ResponseBody //为了返回数据
public R error(ArithmeticException e) {
e.printStackTrace();
return R.error().message("执行了ArithmeticException异常处理..");
}
//自定义异常
@ExceptionHandler(GuliException.class)
@ResponseBody //为了返回数据
public R error(GuliException e) {
log.error(e.getMessage());
e.printStackTrace();
return R.error().code(e.getCode()).message(e.getMsg());
}
}
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
//MetaObjectHandler是Mybatis-plus的公共字段更新类
@Override
public void insertFill(MetaObject metaObject) {
//属性名称,不是字段名称
this.setFieldValByName("gmtCreate", new Date(), metaObject);
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("gmtModified", new Date(), metaObject);
}
}
/MD5工具
public final class MD5 {
public static String encrypt(String strSrc) {
try {
char hexChars[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f' };
byte[] bytes = strSrc.getBytes();
//获取MessageDigest信息摘要对象
MessageDigest md = MessageDigest.getInstance("MD5"); //提供信息摘要算法的功能,它接收任意大小的数据,输出固定长度的哈希值
//更新摘要
md.update(bytes);
//获取哈希值,在MD5算法这,得到的目标字节数组的特点:长度固定为16
bytes = md.digest();
int j = bytes.length;
char[] chars = new char[j * 2];
int k = 0;
for (byte b : bytes) {
//取高四位
chars[k++] = hexChars[b >>> 4 & 0xf];
//取低四位
chars[k++] = hexChars[b & 0xf];
}
return new String(chars);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("MD5加密出错!!+" + e);
}
}
public static void main(String[] args) {
System.out.println(MD5.encrypt("111111"));
}
}
//统一返回结果的类
@Data
public class R {
private Boolean success;
private Integer code;
private String message;
private Map<String, Object> data = new HashMap<String, Object>();
//把构造方法私有
private R() {
}
//成功静态方法
public static R ok() {
R r = new R();
r.setSuccess(true);
r.setCode(20000);
r.setMessage("成功");
return r;
}
//失败静态方法
public static R error() {
R r = new R();
r.setSuccess(false);
r.setCode(20001);
r.setMessage("失败");
return r;
}
public R success(Boolean success){
this.setSuccess(success);
return this;
}
public R message(String message){
this.setMessage(message);
return this;
}
public R code(Integer code){
this.setCode(code);
return this;
}
public R data(String key, Object value){
this.data.put(key, value);
return this;
}
public R data(Map<String, Object> map){
this.setData(map);
return this;
}
}
public class ResponseUtil {
public static void out(HttpServletResponse response, R r) {
//json与对象互换
ObjectMapper mapper = new ObjectMapper();
response.setStatus(HttpStatus.OK.value());
//返回json/utf8格式,过期不影响使用
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
try {
mapper.writeValue(response.getWriter(), r);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@EnableCaching //开启缓存
@Configuration //配置类
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
//RedisTemplate用于对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
//string序列化
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
//json序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
//可见性
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 已过期
// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//新指定序列化方法
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(om);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
//可见性
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//指定序列化类型,否则序列化到redis没有类型,此方法已过期
// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//新指定序列化方法
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
@Configuration//配置类
@EnableSwagger2 //swagger注解
public class SwaggerConfig {
@Bean
public Docket webApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
// 错误路径不监控
.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build();
}
private ApiInfo webApiInfo(){
return new ApiInfoBuilder()
.title("API文档")
.description("微服务接口定义")
.version("1.0")
.contact(new Contact("java", "http://xxx.com", "[email protected]"))
.build();
}
}
2)service_security模块
/**
* 密码加密和校验
*/
@Component
public class DefaultPasswordEncoder implements PasswordEncoder {
//实现密码编码接口
public DefaultPasswordEncoder() {
this(-1);
}
public DefaultPasswordEncoder(int length) {
}
//进行MD5加密
@Override
public String encode(CharSequence charSequence) {
return MD5.encrypt(charSequence.toString());
}
//密码比对
@Override
public boolean matches(CharSequence charSequence, String ecodePassword) {
return ecodePassword.equals(MD5.encrypt(charSequence.toString()));
}
}
/**
* token管理工具
*/
@Component
public class TokenManager {
//token有效时长
private long tokenEcpiration=24*60*60*1000;
//编码密钥
private String tokenSignkey="12345";
//使用JWT根据用户名生成token
public String createToken(String username){
String token= Jwts.builder().setSubject(username)
.setExpiration(new Date(System.currentTimeMillis()+tokenEcpiration))
.signWith(SignatureAlgorithm.HS512,tokenSignkey).compressWith(CompressionCodecs.GZIP).compact();
return token;
}
//根据token字符串得到用户信息
public String getUserInfoFromToken(String token){
String userInfo = Jwts.parser().setSigningKey(tokenSignkey).parseClaimsJws(token).getBody().getSubject();
return userInfo;
}
//删除token
public void removeToken(String token){
}
}
//退出处理器
public class TokenLogoutHandler implements LogoutHandler {
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
public TokenLogoutHandler(TokenManager tokenManager,RedisTemplate redisTemplate){
this.tokenManager=tokenManager;
this.redisTemplate=redisTemplate;
}
@Override
public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
//从header里面获取token
String token=httpServletRequest.getHeader("token");
//token不为空,移出token,从redis删除token
if (token!=null) {
tokenManager.removeToken(token);
//从token获取用户名
String username=tokenManager.getUserInfoFromToken(token);
redisTemplate.delete(username);
}
ResponseUtil.out(httpServletResponse, R.ok());
}
}
//未授权统一处理类
public class UnathEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
ResponseUtil.out(httpServletResponse, R.error());
}
}
/**
* 认证过滤器
*/
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
//security封装的权限管理类
private AuthenticationManager authenticationManager;
public TokenLoginFilter(AuthenticationManager authenticationManager,TokenManager tokenManager,RedisTemplate redisTemplate){
this.authenticationManager=authenticationManager;
this.tokenManager=tokenManager;
this.redisTemplate=redisTemplate;
//提交方式设置
this.setPostOnly(false);
//访问路径
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/acl/login","POST"));
}
//获取表单提交用户名和密码
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
//获取表单提交数据
try {
User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword(),
new ArrayList<>()));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
//认证成功调用的方法
@Override
public void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
//得到用户信息
SecurityUser user = (SecurityUser) authResult.getPrincipal();
//根据用户名生成token
String token = tokenManager.createToken(user.getCurrentUserInfo().getUsername());
//把用户名和用户权限列表放到redis
redisTemplate.opsForValue().set(user.getCurrentUserInfo().getUsername(),user.getPermissionValueList());
//返回token
ResponseUtil.out(response, R.ok().data("token",token));
}
//认证失败调用的方法
@Override
public void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
ResponseUtil.out(response,R.error());
}
}
/**
* 授权过滤器
*/
public class TokenAuthFilter extends BasicAuthenticationFilter {
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
public TokenAuthFilter(AuthenticationManager authenticationManager,TokenManager tokenManager,RedisTemplate redisTemplate) {
super(authenticationManager);
this.tokenManager=tokenManager;
this.redisTemplate=redisTemplate;
}
@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
//获取当前认证成功用户权限信息
UsernamePasswordAuthenticationToken authRequest=getAuthentication(request);
//权限信息存在则放至上下文中
if (authRequest!=null) {
SecurityContextHolder.getContext().setAuthentication(authRequest);
}
chain.doFilter(request,response);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request){
//从header获取token
String token = request.getHeader("token");
if (token!=null) {
//获取用户名
String username = tokenManager.getUserInfoFromToken(token);
//从redis获取对应权限列表
List<String> permissionValueList = (List<String>) redisTemplate.opsForValue().get(username);
Collection<GrantedAuthority> authority=new ArrayList<>();
for (String permissionValue : permissionValueList) {
SimpleGrantedAuthority auth = new SimpleGrantedAuthority((permissionValue));
authority.add(auth);
}
return new UsernamePasswordAuthenticationToken(username,token,authority);
}
return null;
}
}
/**
* 用户信息类
*/
@Data
public class SecurityUser implements UserDetails {
//UserDetails是用户信息核心接口
//当前登录用户
private transient User currentUserInfo;
//当前权限
private List<String> permissionValueList;
public SecurityUser() {
}
public SecurityUser(User user) {
if (user!=null) {
this.currentUserInfo=user;
}
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
//返回权限集
//GrantedAuthority权限接口,实际上是一串字符串
Collection<GrantedAuthority> authorities = new ArrayList<>();
for(String permissionValue : permissionValueList) {
if(StringUtils.isEmpty(permissionValue)) continue;
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
authorities.add(authority);
}
return authorities;
}
@Override
public String getPassword() {
return currentUserInfo.getPassword();
}
@Override
public String getUsername() {
return currentUserInfo.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
/**
* security配置类
*/
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
private DefaultPasswordEncoder defaultPasswordEncoder;
private UserDetailsService userDetailsService;
@Autowired
public TokenWebSecurityConfig(UserDetailsService userDetailsService,DefaultPasswordEncoder defaultPasswordEncoder,
TokenManager tokenManager,RedisTemplate redisTemplate) {
this.userDetailsService=userDetailsService;
this.defaultPasswordEncoder=defaultPasswordEncoder;
this.tokenManager=tokenManager;
this.redisTemplate=redisTemplate;
}
/**
* 配置设置:设置退出的地址和token,redis操作地址
*
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(new UnathEntryPoint())
.and().csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and().logout().logoutUrl("/admin/study/index/logout")
.addLogoutHandler(new TokenLogoutHandler(tokenManager,redisTemplate)).and()
.addFilter(new TokenLoginFilter(authenticationManager(),tokenManager,redisTemplate))
.addFilter(new TokenAuthFilter(authenticationManager(),tokenManager,redisTemplate)).httpBasic();
}
//调用userDetailsService和密码处理
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);
}
//不进行认证的路径,可以直接访问
@Override
public void configure(WebSecurity web){
web.ignoring().antMatchers("/api/**");
}
}
2.service模块编写
1)service_security模块
server.port=8080
#服务名称不能用下划线
spring.application.name=service-security
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123
spring.redis.host=192.168.1.102
spring.redis.port=6379
spring.redis.timeout=180000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
mybatis-plus.mapper-locations=classpath:com/study/securityservice/mapper/xml/*.xml
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
@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("用户不存在");
}
com.study.security.entity.User curUser = new com.study.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;
}
}
@Service
public class IndexServiceImpl implements IndexService {
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Autowired
private PermissionService permissionService;
@Autowired
private RedisTemplate redisTemplate;
/**
* 根据用户名获取用户登录信息
*
* @param username
* @return
*/
public Map<String, Object> getUserInfo(String username) {
Map<String, Object> result = new HashMap<>();
User user = userService.selectByUsername(username);
if (null == user) {
//throw new GuliException(ResultCodeEnum.FETCH_USERINFO_ERROR);
}
//根据用户id获取角色
List<Role> roleList = roleService.selectRoleByUserId(user.getId());
List<String> roleNameList = roleList.stream().map(item -> item.getRoleName()).collect(Collectors.toList());
if(roleNameList.size() == 0) {
//前端框架必须返回一个角色,否则报错,如果没有角色,返回一个空角色
roleNameList.add("");
}
//根据用户id获取操作权限值
List<String> permissionValueList = permissionService.selectPermissionValueByUserId(user.getId());
redisTemplate.opsForValue().set(username, permissionValueList);
result.put("name", user.getUsername());
result.put("avatar", "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
result.put("roles", roleNameList);
result.put("permissionValueList", permissionValueList);
return result;
}
/**
* 根据用户名获取动态菜单
* @param username
* @return
*/
public List<JSONObject> getMenu(String username) {
User user = userService.selectByUsername(username);
//根据用户id获取用户菜单权限
List<JSONObject> permissionList = permissionService.selectPermissionByUserId(user.getId());
return permissionList;
}
}
@Service
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements PermissionService {
@Autowired
private RolePermissionService rolePermissionService;
@Autowired
private UserService userService;
//获取全部菜单
@Override
public List<Permission> queryAllMenu() {
QueryWrapper<Permission> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("id");
List<Permission> permissionList = baseMapper.selectList(wrapper);
List<Permission> result = bulid(permissionList);
return result;
}
//根据角色获取菜单
@Override
public List<Permission> selectAllMenu(String roleId) {
List<Permission> allPermissionList = baseMapper.selectList(new QueryWrapper<Permission>().orderByAsc("CAST(id AS SIGNED)"));
//根据角色id获取角色权限
List<RolePermission> rolePermissionList = rolePermissionService.list(new QueryWrapper<RolePermission>().eq("role_id",roleId));
//转换给角色id与角色权限对应Map对象
// List<String> permissionIdList = rolePermissionList.stream().map(e -> e.getPermissionId()).collect(Collectors.toList());
// allPermissionList.forEach(permission -> {
// if(permissionIdList.contains(permission.getId())) {
// permission.setSelect(true);
// } else {
// permission.setSelect(false);
// }
// });
for (int i = 0; i < allPermissionList.size(); i++) {
Permission permission = allPermissionList.get(i);
for (int m = 0; m < rolePermissionList.size(); m++) {
RolePermission rolePermission = rolePermissionList.get(m);
if(rolePermission.getPermissionId().equals(permission.getId())) {
permission.setSelect(true);
}
}
}
List<Permission> permissionList = bulid(allPermissionList);
return permissionList;
}
//给角色分配权限
@Override
public void saveRolePermissionRealtionShip(String roleId, String[] permissionIds) {
rolePermissionService.remove(new QueryWrapper<RolePermission>().eq("role_id", roleId));
List<RolePermission> rolePermissionList = new ArrayList<>();
for(String permissionId : permissionIds) {
if(StringUtils.isEmpty(permissionId)) continue;
RolePermission rolePermission = new RolePermission();
rolePermission.setRoleId(roleId);
rolePermission.setPermissionId(permissionId);
rolePermissionList.add(rolePermission);
}
rolePermissionService.saveBatch(rolePermissionList);
}
//递归删除菜单
@Override
public void removeChildById(String id) {
List<String> idList = new ArrayList<>();
this.selectChildListById(id, idList);
idList.add(id);
baseMapper.deleteBatchIds(idList);
}
//根据用户id获取用户菜单
@Override
public List<String> selectPermissionValueByUserId(String id) {
List<String> selectPermissionValueList = null;
if(this.isSysAdmin(id)) {
//如果是系统管理员,获取所有权限
selectPermissionValueList = baseMapper.selectAllPermissionValue();
} else {
selectPermissionValueList = baseMapper.selectPermissionValueByUserId(id);
}
return selectPermissionValueList;
}
@Override
public List<JSONObject> selectPermissionByUserId(String userId) {
List<Permission> selectPermissionList = null;
if(this.isSysAdmin(userId)) {
//如果是超级管理员,获取所有菜单
selectPermissionList = baseMapper.selectList(null);
} else {
selectPermissionList = baseMapper.selectPermissionByUserId(userId);
}
List<Permission> permissionList = PermissionHelper.bulid(selectPermissionList);
List<JSONObject> result = MemuHelper.bulid(permissionList);
return result;
}
/**
* 判断用户是否系统管理员
* @param userId
* @return
*/
private boolean isSysAdmin(String userId) {
User user = userService.getById(userId);
if(null != user && "admin".equals(user.getUsername())) {
return true;
}
return false;
}
/**
* 递归获取子节点
* @param id
* @param idList
*/
private void selectChildListById(String id, List<String> idList) {
List<Permission> childList = baseMapper.selectList(new QueryWrapper<Permission>().eq("pid", id).select("id"));
childList.stream().forEach(item -> {
idList.add(item.getId());
this.selectChildListById(item.getId(), idList);
});
}
/**
* 使用递归方法建菜单
* @param treeNodes
* @return
*/
private static List<Permission> bulid(List<Permission> treeNodes) {
List<Permission> trees = new ArrayList<>();
for (Permission treeNode : treeNodes) {
if ("0".equals(treeNode.getPid())) {
treeNode.setLevel(1);
trees.add(findChildren(treeNode,treeNodes));
}
}
return trees;
}
/**
* 递归查找子节点
* @param treeNodes
* @return
*/
private static Permission findChildren(Permission treeNode,List<Permission> treeNodes) {
treeNode.setChildren(new ArrayList<Permission>());
for (Permission it : treeNodes) {
if(treeNode.getId().equals(it.getPid())) {
int level = treeNode.getLevel() + 1;
it.setLevel(level);
if (treeNode.getChildren() == null) {
treeNode.setChildren(new ArrayList<>());
}
treeNode.getChildren().add(findChildren(it,treeNodes));
}
}
return treeNode;
}
//========================递归查询所有菜单================================================
//获取全部菜单
@Override
public List<Permission> queryAllMenuGuli() {
//1 查询菜单表所有数据
QueryWrapper<Permission> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("id");
List<Permission> permissionList = baseMapper.selectList(wrapper);
//2 把查询所有菜单list集合按照要求进行封装
List<Permission> resultList = bulidPermission(permissionList);
return resultList;
}
//把返回所有菜单list集合进行封装的方法
public static List<Permission> bulidPermission(List<Permission> permissionList) {
//创建list集合,用于数据最终封装
List<Permission> finalNode = new ArrayList<>();
//把所有菜单list集合遍历,得到顶层菜单 pid=0菜单,设置level是1
for(Permission permissionNode : permissionList) {
//得到顶层菜单 pid=0菜单
if("0".equals(permissionNode.getPid())) {
//设置顶层菜单的level是1
permissionNode.setLevel(1);
//根据顶层菜单,向里面进行查询子菜单,封装到finalNode里面
finalNode.add(selectChildren(permissionNode,permissionList));
}
}
return finalNode;
}
private static Permission selectChildren(Permission permissionNode, List<Permission> permissionList) {
//1 因为向一层菜单里面放二层菜单,二层里面还要放三层,把对象初始化
permissionNode.setChildren(new ArrayList<Permission>());
//2 遍历所有菜单list集合,进行判断比较,比较id和pid值是否相同
for(Permission it : permissionList) {
//判断 id和pid值是否相同
if(permissionNode.getId().equals(it.getPid())) {
//把父菜单的level值+1
int level = permissionNode.getLevel()+1;
it.setLevel(level);
//如果children为空,进行初始化操作
if(permissionNode.getChildren() == null) {
permissionNode.setChildren(new ArrayList<Permission>());
}
//把查询出来的子菜单放到父菜单里面
permissionNode.getChildren().add(selectChildren(it,permissionList));
}
}
return permissionNode;
}
//============递归删除菜单==================================
@Override
public void removeChildByIdGuli(String id) {
//1 创建list集合,用于封装所有删除菜单id值
List<String> idList = new ArrayList<>();
//2 向idList集合设置删除菜单id
this.selectPermissionChildById(id,idList);
//把当前id封装到list里面
idList.add(id);
baseMapper.deleteBatchIds(idList);
}
//2 根据当前菜单id,查询菜单里面子菜单id,封装到list集合
private void selectPermissionChildById(String id, List<String> idList) {
//查询菜单里面子菜单id
QueryWrapper<Permission> wrapper = new QueryWrapper<>();
wrapper.eq("pid",id);
wrapper.select("id");
List<Permission> childIdList = baseMapper.selectList(wrapper);
//把childIdList里面菜单id值获取出来,封装idList里面,做递归查询
childIdList.stream().forEach(item -> {
//封装idList里面
idList.add(item.getId());
//递归查询
this.selectPermissionChildById(item.getId(),idList);
});
}
//=========================给角色分配菜单=======================
@Override
public void saveRolePermissionRealtionShipGuli(String roleId, String[] permissionIds) {
//roleId角色id
//permissionId菜单id 数组形式
//1 创建list集合,用于封装添加数据
List<RolePermission> rolePermissionList = new ArrayList<>();
//遍历所有菜单数组
for(String perId : permissionIds) {
//RolePermission对象
RolePermission rolePermission = new RolePermission();
rolePermission.setRoleId(roleId);
rolePermission.setPermissionId(perId);
//封装到list集合
rolePermissionList.add(rolePermission);
}
//添加到角色菜单关系表
rolePermissionService.saveBatch(rolePermissionList);
}
}
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {
@Autowired
private UserRoleService userRoleService;
//根据用户获取角色数据
@Override
public Map<String, Object> findRoleByUserId(String userId) {
//查询所有的角色
List<Role> allRolesList =baseMapper.selectList(null);
//根据用户id,查询用户拥有的角色id
List<UserRole> existUserRoleList = userRoleService.list(new QueryWrapper<UserRole>().eq("user_id", userId).select("role_id"));
List<String> existRoleList = existUserRoleList.stream().map(c->c.getRoleId()).collect(Collectors.toList());
//对角色进行分类
List<Role> assignRoles = new ArrayList<Role>();
for (Role role : allRolesList) {
//已分配
if(existRoleList.contains(role.getId())) {
assignRoles.add(role);
}
}
Map<String, Object> roleMap = new HashMap<>();
roleMap.put("assignRoles", assignRoles);
roleMap.put("allRolesList", allRolesList);
return roleMap;
}
//根据用户分配角色
@Override
public void saveUserRoleRealtionShip(String userId, String[] roleIds) {
userRoleService.remove(new QueryWrapper<UserRole>().eq("user_id", userId));
List<UserRole> userRoleList = new ArrayList<>();
for(String roleId : roleIds) {
if(StringUtils.isEmpty(roleId)) continue;
UserRole userRole = new UserRole();
userRole.setUserId(userId);
userRole.setRoleId(roleId);
userRoleList.add(userRole);
}
userRoleService.saveBatch(userRoleList);
}
@Override
public List<Role> selectRoleByUserId(String id) {
//根据用户id拥有的角色id
List<UserRole> userRoleList = userRoleService.list(new QueryWrapper<UserRole>().eq("user_id", id).select("role_id"));
List<String> roleIdList = userRoleList.stream().map(item -> item.getRoleId()).collect(Collectors.toList());
List<Role> roleList = new ArrayList<>();
if(roleIdList.size() > 0) {
roleList = baseMapper.selectBatchIds(roleIdList);
}
return roleList;
}
}
3…infrastrucutre模块编写
1)api_gateway模块
#配置文件
server.port=8090
spring.application.name=service-gateway
#nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
#使用服务发现路由
spring.cloud.gateway.discovery.locator.enabled=true
#配置路由规则
spring.cloud.gateway.routes[0].id=service-security
#设置路由url--lb://注册服务名
spring.cloud.gateway.routes[0].uri=lb://service-security
#具体路径规则
spring.cloud.gateway.routes[0].predicates= Path=/*/acl/**
/**
* 解决跨域问题配置类
*/
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsWebFilter(){
CorsConfiguration config = new CorsConfiguration();
//允许所有方法
config.addAllowedMethod("*");
//允许所有域名
config.addAllowedOrigin("*");
//允许所有Header
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source=new UrlBasedCorsConfigurationSource(new PathPatternParser());
//配置可以访问的地址
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}