Initial commit

This commit is contained in:
yovinchen 2023-06-11 12:16:24 +08:00
parent 57e9cababa
commit a04b3e49a0
14 changed files with 197 additions and 20 deletions

View File

@ -18,7 +18,7 @@ public enum ResultCodeEnum {
FAIL(201, "失败"), FAIL(201, "失败"),
SERVICE_ERROR(2012, "服务异常"), SERVICE_ERROR(2012, "服务异常"),
DATA_ERROR(204, "数据异常"), DATA_ERROR(204, "数据异常"),
LOGIN_ERROR(205, "认证失败"), LOGIN_ERROR(204, "认证失败"),
LOGIN_AUTH(208, "未登陆"), LOGIN_AUTH(208, "未登陆"),
PERMISSION(209, "没有权限"); PERMISSION(209, "没有权限");

View File

@ -32,6 +32,12 @@
<groupId>com.github.xiaoymin</groupId> <groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId> <artifactId>knife4j-spring-boot-starter</artifactId>
</dependency> </dependency>
<!--引入security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>

View File

@ -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.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.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@ -18,6 +20,12 @@ import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice @ControllerAdvice
public class GlobalExceptionHandler { public class GlobalExceptionHandler {
/**
* 全局异常处理执行方法
*
* @param e
* @return
*/
@ExceptionHandler(Exception.class) @ExceptionHandler(Exception.class)
@ResponseBody @ResponseBody
public Result error(Exception e) { public Result error(Exception e) {
@ -25,6 +33,12 @@ public class GlobalExceptionHandler {
return Result.fail(); return Result.fail();
} }
/**
* 特定异常处理类
*
* @param e
* @return
*/
@ExceptionHandler(ArithmeticException.class) @ExceptionHandler(ArithmeticException.class)
@ResponseBody @ResponseBody
public Result error(ArithmeticException e) { public Result error(ArithmeticException e) {
@ -32,10 +46,28 @@ public class GlobalExceptionHandler {
return Result.fail().message("执行了特定异常处理"); return Result.fail().message("执行了特定异常处理");
} }
/**
* 自定义异常处理类
*
* @param e
* @return
*/
@ExceptionHandler(GuiguException.class) @ExceptionHandler(GuiguException.class)
@ResponseBody @ResponseBody
public Result error(GuiguException e) { public Result error(GuiguException e) {
e.printStackTrace(); e.printStackTrace();
return Result.fail().message(e.getMsg()).code(e.getCode()); 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);
}
} }

View File

@ -1,4 +1,4 @@
package com.atguigu.common.execption; package com.atguigu.common.config.execption;
import com.atguigu.common.result.ResultCodeEnum; import com.atguigu.common.result.ResultCodeEnum;
import lombok.Data; import lombok.Data;

View File

@ -38,5 +38,10 @@
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,8 +1,22 @@
package com.atguigu.security.config; 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.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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 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 * ClassName: WebSecurityConfig
@ -14,6 +28,66 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
@Configuration @Configuration
//@EnableWebSecurity是开启SpringSecurity的默认行为 //@EnableWebSecurity是开启SpringSecurity的默认行为
@EnableWebSecurity @EnableWebSecurity
//开启基于方法的安全认证机制在web层的controller启用注解机制的安全确认
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 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");
}
} }

View File

@ -10,7 +10,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
* @author yovinchen * @author yovinchen
* @Create 2023/6/10 23:28 * @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 * @return
* @throws UsernameNotFoundException * @throws UsernameNotFoundException
*/ */
@Override
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
} }

View File

@ -1,10 +1,13 @@
package com.atguigu.security.filter; package com.atguigu.security.filter;
import com.alibaba.fastjson.JSON;
import com.atguigu.common.jwt.JwtHelper; import com.atguigu.common.jwt.JwtHelper;
import com.atguigu.common.result.Result; import com.atguigu.common.result.Result;
import com.atguigu.common.result.ResultCodeEnum; import com.atguigu.common.result.ResultCodeEnum;
import com.atguigu.common.utils.ResponseUtil; import com.atguigu.common.utils.ResponseUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
@ -14,7 +17,9 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/** /**
* ClassName: TokenAuthenticationFilter * ClassName: TokenAuthenticationFilter
@ -26,8 +31,10 @@ import java.util.Collections;
*/ */
public class TokenAuthenticationFilter extends OncePerRequestFilter { public class TokenAuthenticationFilter extends OncePerRequestFilter {
public TokenAuthenticationFilter() { private RedisTemplate redisTemplate;
public TokenAuthenticationFilter(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
} }
@Override @Override
@ -55,8 +62,22 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
if (!StringUtils.isEmpty(token)) { if (!StringUtils.isEmpty(token)) {
String useruame = JwtHelper.getUsername(token); String useruame = JwtHelper.getUsername(token);
logger.info("useruame:" + useruame); logger.info("useruame:" + useruame);
//认证成功
if (!StringUtils.isEmpty(useruame)) { if (!StringUtils.isEmpty(useruame)) {
return new UsernamePasswordAuthenticationToken(useruame, null, Collections.emptyList()); //通过username从reids中获取权限数据
String authString = (String) redisTemplate.opsForValue().get(useruame);
//将redis中获取的字符串权限数据转换为 ArrayList<SimpleGrantedAuthority>
if (!StringUtils.isEmpty(authString)) {
List<Map> mapList = JSON.parseArray(authString, Map.class);
System.out.println(mapList);
List<SimpleGrantedAuthority> 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; return null;

View File

@ -1,5 +1,6 @@
package com.atguigu.security.filter; package com.atguigu.security.filter;
import com.alibaba.fastjson.JSON;
import com.atguigu.common.jwt.JwtHelper; import com.atguigu.common.jwt.JwtHelper;
import com.atguigu.common.result.Result; import com.atguigu.common.result.Result;
import com.atguigu.common.result.ResultCodeEnum; 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.security.custom.CustomUser;
import com.atguigu.vo.system.LoginVo; import com.atguigu.vo.system.LoginVo;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@ -31,13 +33,15 @@ import java.util.Map;
* @Create 2023/6/10 23:37 * @Create 2023/6/10 23:37
*/ */
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter { public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
private RedisTemplate redisTemplate;
// 构造方法 // 构造方法
public TokenLoginFilter(AuthenticationManager authenticationManager) { public TokenLoginFilter(AuthenticationManager authenticationManager, RedisTemplate redisTemplate) {
this.setAuthenticationManager(authenticationManager); this.setAuthenticationManager(authenticationManager);
this.setPostOnly(false); this.setPostOnly(false);
//指定登录接口及提交方式可以指定任意路径 //指定登录接口及提交方式可以指定任意路径
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/system/index/login", "POST")); this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/system/index/login", "POST"));
this.redisTemplate = redisTemplate;
} }
// 登录认证过程 // 登录认证过程
@ -69,6 +73,8 @@ public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
// 生成token // 生成token
String token = JwtHelper.createToken(customUser.getSysUser().getId(), customUser.getSysUser().getUsername()); String token = JwtHelper.createToken(customUser.getSysUser().getId(), customUser.getSysUser().getUsername());
//获取当前用户权限数据放到Reids中key username value权限数据
redisTemplate.opsForValue().set(customUser.getUsername(), JSON.toJSONString(customUser.getAuthorities()));
// 返回 // 返回
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
map.put("token", token); map.put("token", token);
@ -81,7 +87,7 @@ public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
AuthenticationException e) throws IOException, ServletException { AuthenticationException e) throws IOException, ServletException {
if (e.getCause() instanceof RuntimeException) { if (e.getCause() instanceof RuntimeException) {
ResponseUtil.out(response, Result.build(null, ResultCodeEnum.DATA_ERROR)); ResponseUtil.out(response, Result.build(null, ResultCodeEnum.LOGIN_ERROR));
} else { } else {
ResponseUtil.out(response, Result.build(null, ResultCodeEnum.LOGIN_AUTH)); ResponseUtil.out(response, Result.build(null, ResultCodeEnum.LOGIN_AUTH));
} }

View File

@ -2,7 +2,7 @@ package com.atguigu.auth.controller;
import com.atguigu.auth.service.SysMenuService; import com.atguigu.auth.service.SysMenuService;
import com.atguigu.auth.service.SysUserService; 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.jwt.JwtHelper;
import com.atguigu.common.result.Result; import com.atguigu.common.result.Result;
import com.atguigu.common.utils.MD5; import com.atguigu.common.utils.MD5;

View File

@ -11,6 +11,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -63,11 +64,11 @@ public class SysRoleController {
* *
* @return * @return
*/ */
@ApiOperation("查询所有角色") // @ApiOperation("查询所有角色")
@GetMapping("findAll") // @GetMapping("findAll")
public Result findAll() { // public Result findAll() {
return Result.ok(sysRoleService.list()); // return Result.ok(sysRoleService.list());
} // }
/** /**
* 条件分页查询 * 条件分页查询
@ -76,6 +77,7 @@ public class SysRoleController {
* *
* @return * @return
*/ */
@PreAuthorize("hasAuthority('bnt.sysRole.list')")
@ApiOperation("条件分页查询") @ApiOperation("条件分页查询")
@GetMapping("{page}/{limit}") @GetMapping("{page}/{limit}")
public Result pageQueryRole(@PathVariable Long page, public Result pageQueryRole(@PathVariable Long page,
@ -105,6 +107,7 @@ public class SysRoleController {
* @param role * @param role
* @return * @return
*/ */
@PreAuthorize("hasAuthority('bnt.sysRole.add')")
@ApiOperation("添加角色") @ApiOperation("添加角色")
@PostMapping("save") @PostMapping("save")
public Result save(@RequestBody SysRole role) { public Result save(@RequestBody SysRole role) {
@ -123,6 +126,7 @@ public class SysRoleController {
* @param id * @param id
* @return * @return
*/ */
@PreAuthorize("hasAuthority('bnt.sysRole.list')")
@ApiOperation("根据id查询") @ApiOperation("根据id查询")
@GetMapping("get/{id}") @GetMapping("get/{id}")
public Result get(@PathVariable Long id) { public Result get(@PathVariable Long id) {
@ -136,6 +140,7 @@ public class SysRoleController {
* @param role * @param role
* @return * @return
*/ */
@PreAuthorize("hasAuthority('bnt.sysRole.update')")
@ApiOperation("修改角色") @ApiOperation("修改角色")
@PutMapping("update") @PutMapping("update")
public Result update(@RequestBody SysRole role) { public Result update(@RequestBody SysRole role) {
@ -154,6 +159,7 @@ public class SysRoleController {
* @param id * @param id
* @return * @return
*/ */
@PreAuthorize("hasAuthority('bnt.sysRole.remove')")
@ApiOperation("根据id删除") @ApiOperation("根据id删除")
@DeleteMapping("remove/{id}") @DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id) { public Result remove(@PathVariable Long id) {
@ -172,6 +178,7 @@ public class SysRoleController {
* @param idList * @param idList
* @return * @return
*/ */
@PreAuthorize("hasAuthority('bnt.sysRole.remove')")
@ApiOperation("批量删除") @ApiOperation("批量删除")
@DeleteMapping("batchRemove") @DeleteMapping("batchRemove")
public Result batchRemove(@RequestBody List<Long> idList) { public Result batchRemove(@RequestBody List<Long> idList) {

View File

@ -4,7 +4,7 @@ import com.atguigu.auth.mapper.SysMenuMapper;
import com.atguigu.auth.service.SysMenuService; import com.atguigu.auth.service.SysMenuService;
import com.atguigu.auth.service.SysRoleMenuService; import com.atguigu.auth.service.SysRoleMenuService;
import com.atguigu.auth.utils.MenuHelper; 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.SysMenu;
import com.atguigu.model.system.SysRoleMenu; import com.atguigu.model.system.SysRoleMenu;
import com.atguigu.vo.system.AssginMenuVo; import com.atguigu.vo.system.AssginMenuVo;

View File

@ -1,15 +1,18 @@
package com.atguigu.auth.service.impl; package com.atguigu.auth.service.impl;
import com.atguigu.auth.service.SysMenuService;
import com.atguigu.auth.service.SysUserService; import com.atguigu.auth.service.SysUserService;
import com.atguigu.model.system.SysUser; import com.atguigu.model.system.SysUser;
import com.atguigu.security.custom.CustomUser; import com.atguigu.security.custom.CustomUser;
import com.atguigu.security.custom.UserDetailsService; import com.atguigu.security.custom.UserDetailsService;
import org.springframework.beans.factory.annotation.Autowired; 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.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.Collections; import java.util.ArrayList;
import java.util.List;
/** /**
* ClassName: UserDetailsServiceImpl * ClassName: UserDetailsServiceImpl
@ -24,16 +27,26 @@ public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired @Autowired
private SysUserService sysUserService; private SysUserService sysUserService;
@Autowired
private SysMenuService sysMenuService;
@Override @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = sysUserService.getByUsername(username); SysUser sysUser = sysUserService.getByUsername(username);
if(null == sysUser) { if (null == sysUser) {
throw new UsernameNotFoundException("用户名不存在!"); throw new UsernameNotFoundException("用户名不存在!");
} }
if(sysUser.getStatus() == 0) { if (sysUser.getStatus() == 0) {
throw new RuntimeException("账号已停用"); throw new RuntimeException("账号已停用");
} }
return new CustomUser(sysUser, Collections.emptyList()); //根据用户id查询用户操作权限
List<String> userPermsList = sysMenuService.findUserPermsByUserId(sysUser.getId());
//创建List集合封装权限数据
ArrayList<SimpleGrantedAuthority> authList = new ArrayList<>();
for (String perm : userPermsList) {
authList.add(new SimpleGrantedAuthority(perm.trim()));
}
return new CustomUser(sysUser, authList);
} }
} }

View File

@ -16,3 +16,15 @@ spring:
jackson: jackson:
date-format: yyyy-MM-dd HH:mm:ss date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8 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 #最小空闲