SpringSecurity是Spring的安全框架,其核心功能包括认证和授权。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--caffeine 暂时替代redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.19</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.18</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>learning-project-02</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--caffeine 暂时替代redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!--webclient-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.19</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<!--swagger2-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
<!--commons-io-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.13.0</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
<!--hu tool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.18</version>
</dependency>
<dependency>
<groupId>net.sourceforge.tess4j</groupId>
<artifactId>tess4j</artifactId>
<version>5.8.0</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
@ApiModel("用户实体类")
public class User implements Serializable {
private static final long serialVersionUID = -1L;
@ApiModelProperty("用户ID")
@TableId(type = IdType.AUTO)
private Long id;
@ApiModelProperty("用户名称")
private String userName;
@ApiModelProperty("用户密码")
private String passWord;
@ApiModelProperty("是否删除")
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Boolean deleted;
@ApiModelProperty("创建时间")
@TableField(fill = FieldFill.INSERT)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private Date createTime;
@ApiModelProperty("更新时间")
@TableField(fill = FieldFill.UPDATE)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private Date updateTime;
}
@Mapper
@Repository
public interface UserMapper extends BaseMapper<User> {
@Select("select * from user where user_name = #{userName} ")
User selectByUserName(@Param("userName") String userName);
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails {
//用户实体类
private User user;
//权限列表
private List<String> permissions;
//
@JSONField(serialize = false)
private List<SimpleGrantedAuthority> authorities;
public LoginUser(User user, List<String> permissions) {
this.user = user;
this.permissions = permissions;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
if (authorities == null) {
authorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}
return authorities;
}
//返回user用户密码
@Override
public String getPassword() {
return user.getPassWord();
}
//返回user用户名
@Override
public String getUsername() {
return user.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;
}
}
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名获取用户信息
User user = userMapper.selectByUserName(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在");
}
return new LoginUser(user, Arrays.asList());
}
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@Autowired
private UserService userService;
@Autowired
private PasswordEncoder passwordEncoder;
@Test
public void addList() {
User user = User.builder()
.userName("张三").passWord(passwordEncoder.encode("123456"))
.build();
userService.save(user);
}
JWT:轻量级、可扩展、可自包含的身份验证和授权机制
三部分组成:头部(Header)、载荷(Payload)和签名(Signature)
Token:前后分离项目中,生成token令牌,验证token令牌
@Slf4j
public class JwtUtil {
//过期时间
private static final int TTL = 2;
//token秘钥(可自定义配置)
private static final String KEY = "key";
/**
* 创建token
* @param id 用户id
* @return token
*/
public static String createToken(String id) {
DateTime issuedAt = DateTime.now();
DateTime expiresAt = issuedAt.offsetNew(DateField.HOUR, TTL);
HashMap<String, Object> payload = new HashMap<>(8);
payload.put(JWTPayload.ISSUED_AT, issuedAt); //签发时间
payload.put(JWTPayload.EXPIRES_AT, expiresAt); //过期时间
payload.put(JWTPayload.NOT_BEFORE, issuedAt); //生效时间
payload.put("id", id); //自定义载荷(本次使用用户id)
return JWTUtil.createToken(payload, KEY.getBytes());
}
}
public static boolean verifyToken(String token) {
JWT jwt = JWTUtil.parseToken(token).setKey(KEY.getBytes());
return jwt.validate(0);
}
public static String getId(String token) {
return JWTUtil.parseToken(token).setKey(KEY.getBytes()).getPayload().getClaim("id").toString();
}
public static String reset(String token, String id) {
JWT jwt = JWTUtil.parseToken(token).setKey(KEY.getBytes());
long iat = Long.parseLong(jwt.getPayloads().get(JWTPayload.ISSUED_AT).toString());
long exp = Long.parseLong(jwt.getPayloads().get(JWTPayload.EXPIRES_AT).toString());
if (DateUtil.currentSeconds() > (iat + (exp - iat) / 2)) {
return createToken(id);
}
return token;
}
@Configuration
@EnableCaching
public class CacheConfig implements WebMvcConfigurer {
@Bean
public Cache<String, Object> caffeineCache() {
return Caffeine.newBuilder()
.initialCapacity(1000)
.maximumSize(5000)
.expireAfterWrite(24 * 60, TimeUnit.MINUTES)
.build();
}
}
@Test
public void testCaffeineCache() {
User user = User.builder()
.userName("张三").passWord(passwordEncoder.encode("123456"))
.build();
//保存
caffeineCache.put("10101",user);
//获取
User cacheUser = (User)caffeineCache.getIfPresent("1010");
}
@Slf4j
@RestController
public class LoginController {
@Autowired
private LoginService loginService;
@PostMapping("/system/login")
public CommonResponse login(@RequestBody User user) {
return loginService.login(user.getUserName(), user.getPassWord());
}
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//csrf关闭
http.csrf().disable();
//不使用session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//允许登录接口匿名访问
http.authorizeRequests().antMatchers("/system/login").anonymous();
//除上述接口外所有请求需要认证授权
http.authorizeRequests().anyRequest().authenticated();
}
}
@Slf4j
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
Cache<String, Object> caffeineCache;
@Override
public CommonResponse login(String userName, String passWord) {
//获取Authentication对象
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userName, passWord);
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
//认证不通过
if (authenticate == null) {
throw new UsernameNotFoundException("登录失败");
}
//认证通过获用户信息
LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
String userId = loginUser.getUser().getId().toString();
//生成token
String token = JwtUtil.createToken(userId);
//缓存用户信息
caffeineCache.put(userId, loginUser);
//返回
return CommonResponse.success(HttpStatus.OK.value(), "登录成功", token);
}
}
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
Cache<String, Object> caffeineCache;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = request.getHeader("token");
//token不存在,放行(其他过滤器来处理,不是Jwt过滤器的工作)
if (StrUtil.isBlank(token)) {
filterChain.doFilter(request, response);
return;
}
//验证token
try {
if (!JwtUtil.verifyToken(token)) {
WebUtil.renderString(response, JSON.toJSONString(CommonResponse.error("用户凭证已过期")));
return;
}
} catch (Exception e) {
WebUtil.renderString(response, JSON.toJSONString(CommonResponse.error("非法认证")));
return;
}
//获取用户id
String id;
try {
id = JwtUtil.getId(token);
if (StrUtil.isBlank(id)) {
WebUtil.renderString(response, JSON.toJSONString(CommonResponse.error("用户信息不存在")));
return;
}
} catch (Exception e) {
WebUtil.renderString(response, JSON.toJSONString(CommonResponse.error("用户信息异常")));
return;
}
//续写token
String newToken = JwtUtil.reset(token, id);
response.addHeader("token", newToken);
//缓存获取用户信息
LoginUser loginUser = (LoginUser) caffeineCache.getIfPresent(id);
if (Objects.isNull(loginUser)) {
WebUtil.renderString(response, JSON.toJSONString(CommonResponse.error("用户登录已过期")));
return;
}
//设置用户信息
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser,
null, loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
//放行
filterChain.doFilter(request, response);
}
}
public class WebUtil {
public static String renderString(HttpServletResponse response, String msg) {
try {
response.setStatus(HttpStatus.OK.value());
response.setContentType(ContentType.JSON.getValue());
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
response.getWriter().print(msg);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//csrf关闭
http.csrf().disable();
//不使用session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//请求
http.authorizeRequests()
.antMatchers("/system/login").anonymous();
http.authorizeRequests().anyRequest().authenticated();
//token校验
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
@Slf4j
@RestController
public class LoginController {
@Autowired
private LoginService loginService;
@PostMapping("/system/logout")
public CommonResponse logout() {
return loginService.logout();
}
}
@Slf4j
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
Cache<String, Object> caffeineCache;
@Override
public CommonResponse logout() {
//获取Authentication对象
UsernamePasswordAuthenticationToken authenticate = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
//获用户信息
LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
String userId = loginUser.getUser().getId().toString();
//清除缓存用户信息
caffeineCache.invalidate(userId);
//返回
return CommonResponse.success(HttpStatus.OK.value(), "注销成功");
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails {
private User user;
private List<String> permissions;
@JSONField(serialize = false)
private List<SimpleGrantedAuthority> authorities;
public LoginUser(User user, List<String> permissions) {
this.user = user;
this.permissions = permissions;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
if (authorities == null) {
authorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}
return authorities;
}
}
CREATE TABLE `user` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(128) NOT NULL,
`pass_word` varchar(128) DEFAULT NULL,
`deleted` tinyint(1) unsigned DEFAULT '0',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
CREATE TABLE `role` (
`id` bigint(16) NOT NULL,
`name` varchar(255) NOT NULL,
`deleted` tinyint(1) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `menu` (
`id` bigint(16) NOT NULL AUTO_INCREMENT,
`menu_name` varchar(128) NOT NULL,
`perms` varchar(255) DEFAULT NULL,
`deleted` tinyint(1) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user_role` (
`user_id` bigint(16) NOT NULL,
`role_id` bigint(16) NOT NULL,
PRIMARY KEY (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `role_menu` (
`role_id` bigint(16) NOT NULL,
`menu_id` bigint(16) NOT NULL,
PRIMARY KEY (`role_id`,`menu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("menu")
@ApiModel("菜单实体类")
public class Menu implements Serializable {
private static final long serialVersionUID = -1;
@ApiModelProperty("菜单ID")
@TableId(type = IdType.AUTO)
private Long id;
@ApiModelProperty("菜单名称")
private String menuName;
@ApiModelProperty("权限标识")
private String perms;
@ApiModelProperty("是否删除")
@TableLogic
private Boolean deleted;
@ApiModelProperty("创建时间")
@TableField(fill = FieldFill.INSERT)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private Date createTime;
@ApiModelProperty("更新时间")
@TableField(fill = FieldFill.UPDATE)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private Date updateTime;
}
@Mapper
@Repository
public interface MenuMapper extends BaseMapper<Menu> {
@Select("select " +
" distinct m.perms " +
"from " +
" user_role ur " +
" left join role r on ur.role_id = r.id " +
" left join role_menu rm on ur.role_id = rm.role_id " +
" left join menu m on rm.menu_id = m.id " +
"where " +
" ur.user_id = #{userId} ")
List<String> selectPermsByUserId(@Param("userId") Long userId);
}
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Autowired
private MenuMapper menuMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.selectByUserName(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在");
}
List<String> perms = menuMapper.selectPermsByUserId(user.getId());
return new LoginUser(user, perms);
}
}
@GetMapping("/hello1")
@PreAuthorize("hasAnyAuthority('admin','test:list')")
public String hello1() {
return "hello1";
}
@GetMapping("/hello2")
@PreAuthorize("@root.hasAuthority('test:list')")
public String hello2() {
return "hello2";
}
@Component("root")
public class RootAuthorize {
public boolean hasAuthority(String auth) {
//获取当前用户权限
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
List<String> permissions = loginUser.getPermissions();
return permissions.contains(auth);
}
}
@GetMapping("/hello3")
@PreAuthorize("@root.hasAuthority(#request.getRequestURI())")
public String hello3(HttpServletRequest request) {
return "hello3";
}
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
// 返回401 Unauthorized错误码
WebUtil.renderString(response,
JSON.toJSONString(CommonResponse.success(HttpStatus.UNAUTHORIZED.value(), e.getMessage())));
}
}
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException e) throws IOException, ServletException {
//返回403 Forbidden错误
WebUtil.renderString(response,
JSON.toJSONString(CommonResponse.success(HttpStatus.FORBIDDEN.value(), e.getMessage())));
}
}
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(AuthenticationException.class)
public void authenticationException(AuthenticationException e) throws AuthenticationException {
throw e;
}
@ExceptionHandler(AccessDeniedException.class)
public void authenticationException(AccessDeniedException e) throws AccessDeniedException {
throw e;
}
@ExceptionHandler(Exception.class)
public CommonResponse exception(Exception e) {
log.error("[Exception] message: {}", e.getMessage());
return CommonResponse.error("系统异常");
}
@ExceptionHandler(RuntimeException.class)
public CommonResponse runtimeException(RuntimeException e) {
log.error("[Exception] message: {}", e.getMessage());
return CommonResponse.error(e.getMessage());
}
@ExceptionHandler(NullPointerException.class)
public CommonResponse nullPointerException(NullPointerException e) {
log.error("[NullPointerException] message: {}", e.getMessage());
return CommonResponse.error("空指针异常");
}
@ExceptionHandler(IndexOutOfBoundsException.class)
public CommonResponse indexOutOfBoundsException(IndexOutOfBoundsException e) {
log.error("[IndexOutOfBoundsException] message: {}", e.getMessage());
return CommonResponse.error("下标越界异常");
}
@ExceptionHandler(NoSuchMethodException.class)
public CommonResponse noSuchMethodException(NoSuchMethodException e) {
log.error("[NoSuchMethodException] message: {}", e.getMessage());
return CommonResponse.error("未找到对应方法异常");
}
@ExceptionHandler(IOException.class)
public CommonResponse ioException(IOException e) {
log.error("[IOEXCEPTION] message: {}", e.getMessage());
return CommonResponse.error("IO异常");
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public CommonResponse methodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error("[MethodArgumentNotValidException] message: {}", e.getMessage());
return CommonResponse.error("参数校验异常");
}
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//csrf关闭
http.csrf().disable();
//不使用session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//请求
http.authorizeRequests()
.antMatchers("/system/login").anonymous();
http.authorizeRequests().anyRequest().authenticated();
http.formLogin().loginPage("/").permitAll();
//token校验
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
//异常处理
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//csrf关闭
http.csrf().disable();
//不使用session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//请求
http.authorizeRequests()
.antMatchers("/system/login").anonymous();
http.authorizeRequests().anyRequest().authenticated();
http.formLogin().loginPage("/").permitAll();
//token校验
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
//异常处理
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
//跨域
http.cors().disable();
}
@Data
@Builder
@ApiModel("API通用返回数据实体类")
public class CommonResponse implements Serializable {
private static final long serialVersionUID = -1;
@ApiModelProperty("代码")
private int code;
@ApiModelProperty("提示信息")
private String msg;
@ApiModelProperty("数据")
private Object data;
public CommonResponse(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static CommonResponse success() {
return new CommonResponse(HttpStatus.OK.value(), HttpStatus.OK.name(), null);
}
public static CommonResponse success(String msg) {
return new CommonResponse(HttpStatus.OK.value(), msg, null);
}
public static CommonResponse success(int code, String msg) {
return new CommonResponse(code, msg, null);
}
public static CommonResponse success(Object data) {
return new CommonResponse(HttpStatus.OK.value(), HttpStatus.OK.name(), data);
}
public static CommonResponse success(int code, String msg, Object data) {
return new CommonResponse(code, msg, data);
}
public static CommonResponse error() {
return new CommonResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.name(), null);
}
public static CommonResponse error(String msg) {
return new CommonResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg, null);
}
public static CommonResponse error(int code, String msg) {
return new CommonResponse(code, msg, null);
}
}
@Component
@WebFilter(urlPatterns = "/*")
public class XssFilter implements Filter {
public static final List<String> EXCLUDE_URL_PATTERNS = Arrays.asList("/error", "/actuator",
"/swagger-ui.html", "/swagger-resources", "/webjars", "/v2/api-docs");
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if (StrUtil.isNotBlank(request.getRequestURI()) && EXCLUDE_URL_PATTERNS.contains(
StrUtil.sub(
request.getRequestURI().replace("//", "/"), 0,
StrUtil.indexOf(request.getRequestURI().replace("//", "/"), '/', 1) > 0 ?
StrUtil.indexOf(request.getRequestURI().replace("//", "/"), '/', 1) :
request.getRequestURI().replace("//", "/").length()))
) {
filterChain.doFilter(request, response);
} else {
XssHttpServletRequestWrapper wrapper = new XssHttpServletRequestWrapper(request);
filterChain.doFilter(wrapper, response);
}
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
private byte[] requestBody;
public XssHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
try {
requestBody = StreamUtils.copyToByteArray(request.getInputStream());
} catch (Exception e) {
throw new IOException("Xss拦截异常");
}
}
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
if (StrUtil.isNotBlank(value)) {
value = HtmlUtil.cleanHtmlTag(value).trim();
}
return value;
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
if (StrUtil.isNotBlank(value)) {
value = HtmlUtil.cleanHtmlTag(value).trim();
}
return value;
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values == null || values.length <= 0) {
return super.getParameterValues(name);
}
for (int i = 0; i < values.length; i++) {
if (StrUtil.isNotBlank(values[i])) {
values[i] = HtmlUtil.cleanHtmlTag(values[i]).trim();
}
}
return values;
}
@Override
public Map<String, String[]> getParameterMap() {
LinkedHashMap<String, String[]> map = new LinkedHashMap<>();
Map<String, String[]> parameterMap = super.getParameterMap();
if (parameterMap == null) {
return super.getParameterMap();
}
for (String key : parameterMap.keySet()) {
String[] values = parameterMap.get(key);
for (int i = 0; i < values.length; i++) {
if (StrUtil.isNotBlank(values[i])) {
values[i] = HtmlUtil.cleanHtmlTag(values[i]).trim();
}
}
map.put(key, values);
}
return map;
}
@Override
public ServletInputStream getInputStream() throws IOException {
//空直接返回
if (requestBody == null) {
requestBody = new byte[0];
}
//非json直接返回
if (!StrUtil.startWithIgnoreCase(super.getHeader(HttpHeaders.CONTENT_TYPE), MediaType.APPLICATION_JSON_VALUE)) {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody);
return getServletInputStream(byteArrayInputStream);
}
StringBuilder body = new StringBuilder();
ByteArrayInputStream in = new ByteArrayInputStream(requestBody);
InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8);
BufferedReader buffer = new BufferedReader(reader);
String line = buffer.readLine();
while (line != null) {
body.append(line);
line = buffer.readLine();
}
buffer.close();
reader.close();
in.close();
Map<String, Object> result = new LinkedHashMap<>();
Map<String, Object> jsonMap = JSONUtil.parseObj(body.toString());
for (String key : jsonMap.keySet()) {
Object value = jsonMap.get(key);
if (value instanceof String) {
if (StrUtil.isNotBlank(value.toString())) {
result.put(key, HtmlUtil.cleanHtmlTag(value.toString()).trim());
} else {
result.put(key, value);
}
} else {
result.put(key, value);
}
}
String json = JSONUtil.toJsonStr(result);
ByteArrayInputStream bain = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
return getServletInputStream(bain);
}
private ServletInputStream getServletInputStream(ByteArrayInputStream bain) {
return new ServletInputStream() {
@Override
public int read() {
return bain.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}