From a04b3e49a0c7e58ec16ece584dce24d48f915ee9 Mon Sep 17 00:00:00 2001 From: yovinchen Date: Sun, 11 Jun 2023 12:16:24 +0800 Subject: [PATCH] Initial commit --- .../atguigu/common/result/ResultCodeEnum.java | 2 +- common/service-util/pom.xml | 6 ++ .../execption/GlobalExceptionHandler.java | 34 ++++++++- .../execption/GuiguException.java | 2 +- common/spring-security/pom.xml | 5 ++ .../security/config/WebSecurityConfig.java | 74 +++++++++++++++++++ .../security/custom/UserDetailsService.java | 3 +- .../filter/TokenAuthenticationFilter.java | 27 ++++++- .../security/filter/TokenLoginFilter.java | 10 ++- .../auth/controller/IndexController.java | 2 +- .../auth/controller/SysRoleController.java | 17 +++-- .../auth/service/impl/SysMenuServiceImpl.java | 2 +- .../service/impl/UserDetailsServiceImpl.java | 21 +++++- .../src/main/resources/application-dev.yml | 12 +++ 14 files changed, 197 insertions(+), 20 deletions(-) rename common/service-util/src/main/java/com/atguigu/common/{ => config}/execption/GlobalExceptionHandler.java (59%) rename common/service-util/src/main/java/com/atguigu/common/{ => config}/execption/GuiguException.java (95%) diff --git a/common/common-util/src/main/java/com/atguigu/common/result/ResultCodeEnum.java b/common/common-util/src/main/java/com/atguigu/common/result/ResultCodeEnum.java index 679f061..e1612e8 100644 --- a/common/common-util/src/main/java/com/atguigu/common/result/ResultCodeEnum.java +++ b/common/common-util/src/main/java/com/atguigu/common/result/ResultCodeEnum.java @@ -18,7 +18,7 @@ public enum ResultCodeEnum { FAIL(201, "失败"), SERVICE_ERROR(2012, "服务异常"), DATA_ERROR(204, "数据异常"), - LOGIN_ERROR(205, "认证失败"), + LOGIN_ERROR(204, "认证失败"), LOGIN_AUTH(208, "未登陆"), PERMISSION(209, "没有权限"); diff --git a/common/service-util/pom.xml b/common/service-util/pom.xml index 51dc7c5..08a4497 100644 --- a/common/service-util/pom.xml +++ b/common/service-util/pom.xml @@ -32,6 +32,12 @@ com.github.xiaoymin knife4j-spring-boot-starter + + + org.springframework.boot + spring-boot-starter-security + provided + diff --git a/common/service-util/src/main/java/com/atguigu/common/execption/GlobalExceptionHandler.java b/common/service-util/src/main/java/com/atguigu/common/config/execption/GlobalExceptionHandler.java similarity index 59% rename from common/service-util/src/main/java/com/atguigu/common/execption/GlobalExceptionHandler.java rename to common/service-util/src/main/java/com/atguigu/common/config/execption/GlobalExceptionHandler.java index aae03eb..37e1152 100644 --- a/common/service-util/src/main/java/com/atguigu/common/execption/GlobalExceptionHandler.java +++ b/common/service-util/src/main/java/com/atguigu/common/config/execption/GlobalExceptionHandler.java @@ -1,6 +1,8 @@ -package com.atguigu.common.execption; +package com.atguigu.common.config.execption; import com.atguigu.common.result.Result; +import com.atguigu.common.result.ResultCodeEnum; +import org.springframework.security.access.AccessDeniedException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @@ -18,6 +20,12 @@ import org.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice public class GlobalExceptionHandler { + /** + * 全局异常处理,执行方法 + * + * @param e + * @return + */ @ExceptionHandler(Exception.class) @ResponseBody public Result error(Exception e) { @@ -25,6 +33,12 @@ public class GlobalExceptionHandler { return Result.fail(); } + /** + * 特定异常处理类 + * + * @param e + * @return + */ @ExceptionHandler(ArithmeticException.class) @ResponseBody public Result error(ArithmeticException e) { @@ -32,10 +46,28 @@ public class GlobalExceptionHandler { return Result.fail().message("执行了特定异常处理"); } + /** + * 自定义异常处理类 + * + * @param e + * @return + */ @ExceptionHandler(GuiguException.class) @ResponseBody public Result error(GuiguException e) { e.printStackTrace(); return Result.fail().message(e.getMsg()).code(e.getCode()); } + + /** + * spring security异常 + * + * @param e + * @return + */ + @ExceptionHandler(AccessDeniedException.class) + @ResponseBody + public Result error(AccessDeniedException e) throws AccessDeniedException { + return Result.build(null, ResultCodeEnum.PERMISSION); + } } diff --git a/common/service-util/src/main/java/com/atguigu/common/execption/GuiguException.java b/common/service-util/src/main/java/com/atguigu/common/config/execption/GuiguException.java similarity index 95% rename from common/service-util/src/main/java/com/atguigu/common/execption/GuiguException.java rename to common/service-util/src/main/java/com/atguigu/common/config/execption/GuiguException.java index f3631b1..d76a370 100644 --- a/common/service-util/src/main/java/com/atguigu/common/execption/GuiguException.java +++ b/common/service-util/src/main/java/com/atguigu/common/config/execption/GuiguException.java @@ -1,4 +1,4 @@ -package com.atguigu.common.execption; +package com.atguigu.common.config.execption; import com.atguigu.common.result.ResultCodeEnum; import lombok.Data; diff --git a/common/spring-security/pom.xml b/common/spring-security/pom.xml index 7bf3fee..7178b3c 100644 --- a/common/spring-security/pom.xml +++ b/common/spring-security/pom.xml @@ -38,5 +38,10 @@ spring-boot-starter-web provided + + + org.springframework.boot + spring-boot-starter-data-redis + diff --git a/common/spring-security/src/main/java/com/atguigu/security/config/WebSecurityConfig.java b/common/spring-security/src/main/java/com/atguigu/security/config/WebSecurityConfig.java index 7c3edc7..89402ac 100644 --- a/common/spring-security/src/main/java/com/atguigu/security/config/WebSecurityConfig.java +++ b/common/spring-security/src/main/java/com/atguigu/security/config/WebSecurityConfig.java @@ -1,8 +1,22 @@ package com.atguigu.security.config; +import com.atguigu.security.custom.CustomMd5PasswordEncoder; +import com.atguigu.security.filter.TokenAuthenticationFilter; +import com.atguigu.security.filter.TokenLoginFilter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * ClassName: WebSecurityConfig @@ -14,6 +28,66 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur @Configuration //@EnableWebSecurity是开启SpringSecurity的默认行为 @EnableWebSecurity +//开启基于方法的安全认证机制,在web层的controller启用注解机制的安全确认 +@EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + /** + * 装载的是 org.springframework.security.core.userdetails.UserDetailsService; + */ + @Autowired + private UserDetailsService userDetailsService; + + @Autowired + private CustomMd5PasswordEncoder customMd5PasswordEncoder; + + @Autowired + private RedisTemplate redisTemplate; + + @Bean + @Override + protected AuthenticationManager authenticationManager() throws Exception { + return super.authenticationManager(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + // 这是配置的关键,决定哪些接口开启防护,哪些接口绕过防护 + http + //关闭csrf跨站请求伪造 + .csrf().disable() + // 开启跨域以便前端调用接口 + .cors().and() + .authorizeRequests() + // 指定某些接口不需要通过验证即可访问。登陆接口肯定是不需要认证的 + .antMatchers("/admin/system/index/login").permitAll() + // 这里意思是其它所有接口需要认证才能访问 + .anyRequest().authenticated() + .and() + //TokenAuthenticationFilter放到UsernamePasswordAuthenticationFilter的前面,这样做就是为了除了登录的时候去查询数据库外,其他时候都用token进行认证。 + .addFilterBefore(new TokenAuthenticationFilter(redisTemplate), UsernamePasswordAuthenticationFilter.class) + .addFilter(new TokenLoginFilter(authenticationManager(), redisTemplate)); + + //禁用session + http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + // 指定UserDetailService和加密器 + auth.userDetailsService(userDetailsService) + .passwordEncoder(customMd5PasswordEncoder); + } + + /** + * 配置哪些请求不拦截 + * 排除swagger相关请求 + * + * @param web + * @throws Exception + */ + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring().antMatchers("/favicon.ico", "/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**", "/doc.html"); + } } diff --git a/common/spring-security/src/main/java/com/atguigu/security/custom/UserDetailsService.java b/common/spring-security/src/main/java/com/atguigu/security/custom/UserDetailsService.java index 2463548..e8bef49 100644 --- a/common/spring-security/src/main/java/com/atguigu/security/custom/UserDetailsService.java +++ b/common/spring-security/src/main/java/com/atguigu/security/custom/UserDetailsService.java @@ -10,7 +10,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; * @author yovinchen * @Create 2023/6/10 23:28 */ -public interface UserDetailsService { +public interface UserDetailsService extends org.springframework.security.core.userdetails.UserDetailsService { /** * 根据用户名获取用户对象(获取不到直接抛异常) @@ -19,5 +19,6 @@ public interface UserDetailsService { * @return * @throws UsernameNotFoundException */ + @Override UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; } diff --git a/common/spring-security/src/main/java/com/atguigu/security/filter/TokenAuthenticationFilter.java b/common/spring-security/src/main/java/com/atguigu/security/filter/TokenAuthenticationFilter.java index a215cc2..d60425e 100644 --- a/common/spring-security/src/main/java/com/atguigu/security/filter/TokenAuthenticationFilter.java +++ b/common/spring-security/src/main/java/com/atguigu/security/filter/TokenAuthenticationFilter.java @@ -1,10 +1,13 @@ package com.atguigu.security.filter; +import com.alibaba.fastjson.JSON; import com.atguigu.common.jwt.JwtHelper; import com.atguigu.common.result.Result; import com.atguigu.common.result.ResultCodeEnum; import com.atguigu.common.utils.ResponseUtil; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; @@ -14,7 +17,9 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.util.Collections; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; /** * ClassName: TokenAuthenticationFilter @@ -26,8 +31,10 @@ import java.util.Collections; */ public class TokenAuthenticationFilter extends OncePerRequestFilter { - public TokenAuthenticationFilter() { + private RedisTemplate redisTemplate; + public TokenAuthenticationFilter(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; } @Override @@ -55,8 +62,22 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { if (!StringUtils.isEmpty(token)) { String useruame = JwtHelper.getUsername(token); logger.info("useruame:" + useruame); + //认证成功 if (!StringUtils.isEmpty(useruame)) { - return new UsernamePasswordAuthenticationToken(useruame, null, Collections.emptyList()); + //通过username从reids中获取权限数据 + String authString = (String) redisTemplate.opsForValue().get(useruame); + //将redis中获取的字符串权限数据转换为 ArrayList + if (!StringUtils.isEmpty(authString)) { + List mapList = JSON.parseArray(authString, Map.class); + System.out.println(mapList); + List authList = new ArrayList<>(); + for (Map map : mapList) { + authList.add(new SimpleGrantedAuthority((String) map.get("authority"))); + } + return new UsernamePasswordAuthenticationToken(useruame, null, authList); + } else { + return new UsernamePasswordAuthenticationToken(useruame, null, new ArrayList<>()); + } } } return null; diff --git a/common/spring-security/src/main/java/com/atguigu/security/filter/TokenLoginFilter.java b/common/spring-security/src/main/java/com/atguigu/security/filter/TokenLoginFilter.java index 631e0a7..a23bd5c 100644 --- a/common/spring-security/src/main/java/com/atguigu/security/filter/TokenLoginFilter.java +++ b/common/spring-security/src/main/java/com/atguigu/security/filter/TokenLoginFilter.java @@ -1,5 +1,6 @@ package com.atguigu.security.filter; +import com.alibaba.fastjson.JSON; import com.atguigu.common.jwt.JwtHelper; import com.atguigu.common.result.Result; import com.atguigu.common.result.ResultCodeEnum; @@ -7,6 +8,7 @@ import com.atguigu.common.utils.ResponseUtil; import com.atguigu.security.custom.CustomUser; import com.atguigu.vo.system.LoginVo; import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -31,13 +33,15 @@ import java.util.Map; * @Create 2023/6/10 23:37 */ public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter { + private RedisTemplate redisTemplate; // 构造方法 - public TokenLoginFilter(AuthenticationManager authenticationManager) { + public TokenLoginFilter(AuthenticationManager authenticationManager, RedisTemplate redisTemplate) { this.setAuthenticationManager(authenticationManager); this.setPostOnly(false); //指定登录接口及提交方式,可以指定任意路径 this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/system/index/login", "POST")); + this.redisTemplate = redisTemplate; } // 登录认证过程 @@ -69,6 +73,8 @@ public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter { // 生成token String token = JwtHelper.createToken(customUser.getSysUser().getId(), customUser.getSysUser().getUsername()); + //获取当前用户权限数据,放到Reids中,key: username value:权限数据 + redisTemplate.opsForValue().set(customUser.getUsername(), JSON.toJSONString(customUser.getAuthorities())); // 返回 Map map = new HashMap<>(); map.put("token", token); @@ -81,7 +87,7 @@ public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter { AuthenticationException e) throws IOException, ServletException { if (e.getCause() instanceof RuntimeException) { - ResponseUtil.out(response, Result.build(null, ResultCodeEnum.DATA_ERROR)); + ResponseUtil.out(response, Result.build(null, ResultCodeEnum.LOGIN_ERROR)); } else { ResponseUtil.out(response, Result.build(null, ResultCodeEnum.LOGIN_AUTH)); } diff --git a/service-oa/src/main/java/com/atguigu/auth/controller/IndexController.java b/service-oa/src/main/java/com/atguigu/auth/controller/IndexController.java index ab68c21..e824bcb 100644 --- a/service-oa/src/main/java/com/atguigu/auth/controller/IndexController.java +++ b/service-oa/src/main/java/com/atguigu/auth/controller/IndexController.java @@ -2,7 +2,7 @@ package com.atguigu.auth.controller; import com.atguigu.auth.service.SysMenuService; import com.atguigu.auth.service.SysUserService; -import com.atguigu.common.execption.GuiguException; +import com.atguigu.common.config.execption.GuiguException; import com.atguigu.common.jwt.JwtHelper; import com.atguigu.common.result.Result; import com.atguigu.common.utils.MD5; diff --git a/service-oa/src/main/java/com/atguigu/auth/controller/SysRoleController.java b/service-oa/src/main/java/com/atguigu/auth/controller/SysRoleController.java index 825fe0c..2a18dd9 100644 --- a/service-oa/src/main/java/com/atguigu/auth/controller/SysRoleController.java +++ b/service-oa/src/main/java/com/atguigu/auth/controller/SysRoleController.java @@ -11,6 +11,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; @@ -63,11 +64,11 @@ public class SysRoleController { * * @return */ - @ApiOperation("查询所有角色") - @GetMapping("findAll") - public Result findAll() { - return Result.ok(sysRoleService.list()); - } +// @ApiOperation("查询所有角色") +// @GetMapping("findAll") +// public Result findAll() { +// return Result.ok(sysRoleService.list()); +// } /** * 条件分页查询 @@ -76,6 +77,7 @@ public class SysRoleController { * * @return */ + @PreAuthorize("hasAuthority('bnt.sysRole.list')") @ApiOperation("条件分页查询") @GetMapping("{page}/{limit}") public Result pageQueryRole(@PathVariable Long page, @@ -105,6 +107,7 @@ public class SysRoleController { * @param role * @return */ + @PreAuthorize("hasAuthority('bnt.sysRole.add')") @ApiOperation("添加角色") @PostMapping("save") public Result save(@RequestBody SysRole role) { @@ -123,6 +126,7 @@ public class SysRoleController { * @param id * @return */ + @PreAuthorize("hasAuthority('bnt.sysRole.list')") @ApiOperation("根据id查询") @GetMapping("get/{id}") public Result get(@PathVariable Long id) { @@ -136,6 +140,7 @@ public class SysRoleController { * @param role * @return */ + @PreAuthorize("hasAuthority('bnt.sysRole.update')") @ApiOperation("修改角色") @PutMapping("update") public Result update(@RequestBody SysRole role) { @@ -154,6 +159,7 @@ public class SysRoleController { * @param id * @return */ + @PreAuthorize("hasAuthority('bnt.sysRole.remove')") @ApiOperation("根据id删除") @DeleteMapping("remove/{id}") public Result remove(@PathVariable Long id) { @@ -172,6 +178,7 @@ public class SysRoleController { * @param idList * @return */ + @PreAuthorize("hasAuthority('bnt.sysRole.remove')") @ApiOperation("批量删除") @DeleteMapping("batchRemove") public Result batchRemove(@RequestBody List idList) { diff --git a/service-oa/src/main/java/com/atguigu/auth/service/impl/SysMenuServiceImpl.java b/service-oa/src/main/java/com/atguigu/auth/service/impl/SysMenuServiceImpl.java index 422781b..5e6f9d7 100644 --- a/service-oa/src/main/java/com/atguigu/auth/service/impl/SysMenuServiceImpl.java +++ b/service-oa/src/main/java/com/atguigu/auth/service/impl/SysMenuServiceImpl.java @@ -4,7 +4,7 @@ import com.atguigu.auth.mapper.SysMenuMapper; import com.atguigu.auth.service.SysMenuService; import com.atguigu.auth.service.SysRoleMenuService; import com.atguigu.auth.utils.MenuHelper; -import com.atguigu.common.execption.GuiguException; +import com.atguigu.common.config.execption.GuiguException; import com.atguigu.model.system.SysMenu; import com.atguigu.model.system.SysRoleMenu; import com.atguigu.vo.system.AssginMenuVo; diff --git a/service-oa/src/main/java/com/atguigu/auth/service/impl/UserDetailsServiceImpl.java b/service-oa/src/main/java/com/atguigu/auth/service/impl/UserDetailsServiceImpl.java index e173172..8bd4b15 100644 --- a/service-oa/src/main/java/com/atguigu/auth/service/impl/UserDetailsServiceImpl.java +++ b/service-oa/src/main/java/com/atguigu/auth/service/impl/UserDetailsServiceImpl.java @@ -1,15 +1,18 @@ package com.atguigu.auth.service.impl; +import com.atguigu.auth.service.SysMenuService; import com.atguigu.auth.service.SysUserService; import com.atguigu.model.system.SysUser; import com.atguigu.security.custom.CustomUser; import com.atguigu.security.custom.UserDetailsService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; -import java.util.Collections; +import java.util.ArrayList; +import java.util.List; /** * ClassName: UserDetailsServiceImpl @@ -24,16 +27,26 @@ public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private SysUserService sysUserService; + @Autowired + private SysMenuService sysMenuService; + @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { SysUser sysUser = sysUserService.getByUsername(username); - if(null == sysUser) { + if (null == sysUser) { throw new UsernameNotFoundException("用户名不存在!"); } - if(sysUser.getStatus() == 0) { + if (sysUser.getStatus() == 0) { throw new RuntimeException("账号已停用"); } - return new CustomUser(sysUser, Collections.emptyList()); + //根据用户id查询用户操作权限 + List userPermsList = sysMenuService.findUserPermsByUserId(sysUser.getId()); + //创建List集合,封装权限数据 + ArrayList authList = new ArrayList<>(); + for (String perm : userPermsList) { + authList.add(new SimpleGrantedAuthority(perm.trim())); + } + return new CustomUser(sysUser, authList); } } diff --git a/service-oa/src/main/resources/application-dev.yml b/service-oa/src/main/resources/application-dev.yml index 2fd38d7..dc183dd 100644 --- a/service-oa/src/main/resources/application-dev.yml +++ b/service-oa/src/main/resources/application-dev.yml @@ -16,3 +16,15 @@ spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 + redis: + host: localhost + port: 6379 + database: 0 + timeout: 1800000 + password: + jedis: + pool: + max-active: 20 #最大连接数 + max-wait: -1 #最大阻塞等待时间(负数表示没限制) + max-idle: 5 #最大空闲 + min-idle: 0 #最小空闲