当前位置: 首页 > news >正文

基于Springboot+UniApp+Ai实现模拟面试小工具五:权限校验参数校验及日志功能实现

  介绍本项目后端的操作日志记录功能/登录及权限校验功能及参数合法性校验功能实现。
在这里插入图片描述
源码下载: 点击下载
讲解视频:

Uniapp+Springboot+Kimi实现模拟面试小程序-SpringSecurity配置引入上

一.登录及权限校验功能实现

  本项目使用sercurity实现系统权限校验及登录管理,需配置权限校验及登录修改逻辑。

1.1 登录功能实现

  1. User
package com.junjunjun.aiwork.entity.user;import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.junjunjun.aiwork.entity.BaseEntity;
import com.junjunjun.aiwork.entity.system.Role;
import com.junjunjun.aiwork.validate.Create;
import com.junjunjun.aiwork.validate.Update;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.*;
import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;/*** 用户表*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Accessors(chain = true)
@EqualsAndHashCode( callSuper =true)
@TableName(value="junjunjun_user",resultMap="userResultMap")
public class User extends BaseEntity implements UserDetails {/*** 账号*/@NotBlank(message = "账号信息不能为空",groups = {Update.class, Create.class})private String account;/*** 姓名*/@NotBlank(message = "姓名信息不能为空",groups = {Update.class, Create.class})private String name;/*** 头像*/private String header;/*** 性别*/private String sex;/*** 电话*/private String phone;/*** 密码*/@NotBlank(message = "密码不能为空",groups = { Create.class})private String password;/*** 小程序的openid*/private String openid;/*** 角色数据*/@TableField(exist = false)@NotEmpty(message = "角色信息不能为空",groups = {Update.class, Create.class})private List<Role> roles;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {List<GrantedAuthority> auths = new ArrayList<>();if(roles!=null){roles.forEach(item->{auths.add(item);if(item.getMenus()!=null){item.getMenus().forEach(menuItem->{auths.add(menuItem);});}});}return auths;}@Overridepublic String getUsername() {return account;}
}
  1. IUserService
package com.junjunjun.aiwork.service.user;import com.junjunjun.aiwork.entity.user.User;
import com.junjunjun.aiwork.service.IBaseService;
import org.springframework.security.core.userdetails.UserDetailsService;import java.util.Date;public interface IUserService extends IBaseService<User>, UserDetailsService {/*** 修改密码* @param oldpwd 旧密码* @param newpwd 新密码*/public void updatePwd(String oldpwd,String newpwd) throws Exception;/*** 重置用户密码* @param user* @param newpwd*/public void resetPwd(User user,String newpwd);/*** 修改个人信息* @param t*/void updateInfo(User t);/*** 通过openid获取用户信息* @param openid* @return*/User getByOpenId(String openid);/*** 用户注册* @param user*/void register(User user);/*** 查询电话号码对应的用户数据* @param phone* @return*/User loadUserByPhone(String phone);/*** 统计角色对应的用户数量* @param roleId* @return*/long countByRole(long roleId);Long countByDayAndRole(Date day, long roleId);
}
  1. UserService
package com.junjunjun.aiwork.service.user.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.junjunjun.aiwork.Log;
import com.junjunjun.aiwork.UserUtils;
import com.junjunjun.aiwork.dao.system.MenuDao;
import com.junjunjun.aiwork.dao.user.UserDao;
import com.junjunjun.aiwork.dao.user.UserRoleItemDao;
import com.junjunjun.aiwork.entity.system.Menu;
import com.junjunjun.aiwork.entity.system.Role;
import com.junjunjun.aiwork.entity.system.RoleMenuItem;
import com.junjunjun.aiwork.entity.system.Setting;
import com.junjunjun.aiwork.entity.user.User;
import com.junjunjun.aiwork.entity.user.UserRoleItem;
import com.junjunjun.aiwork.service.impl.BaseService;
import com.junjunjun.aiwork.service.user.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;import java.util.Date;@Service
public class UserService extends BaseService<User,UserDao> implements IUserService {@Autowiredprivate MenuDao menuDao;@Autowiredprivate PasswordEncoder passwordEncoder;@Autowiredprivate UserRoleItemDao userRoleItemDao;@Override@Transactional(rollbackFor = Exception.class)public boolean save(User entity) {//保存数据的时候对密码进行加密entity.setPassword(passwordEncoder.encode(entity.getPassword()));boolean result =  super.save(entity);if(result){if(entity.getRoles()!=null){for (Role role:entity.getRoles()){UserRoleItem item = new UserRoleItem();item.setRoleId(role.getId());item.setUserId(entity.getId());userRoleItemDao.insert(item);}}}return  result;}@Overridepublic boolean update(User user) {boolean result =  super.update(user);if(result){
//            QueryWrapper<UserRoleItem> queryWrapper = new QueryWrapper<>();
//            queryWrapper.eq("user_id",user.getId());userRoleItemDao.deleteByUserId(user.getId());if(user.getRoles()!=null){for (Role role:user.getRoles()){UserRoleItem item = new UserRoleItem();item.setRoleId(role.getId());item.setUserId(user.getId());userRoleItemDao.insert(item);}}}return  result;}@Overrideprotected UpdateWrapper<User> initUpdateWrapper(User user) {UpdateWrapper<User> uw = super.initUpdateWrapper(user);if(!ObjectUtils.isEmpty(user.getName())) {uw.set("name", user.getName());}if(!ObjectUtils.isEmpty(user.getPhone())) {uw.set("phone", user.getPhone());}if(!ObjectUtils.isEmpty(user.getSex())){uw.set("sex",user.getSex());}return uw;}@Overridepublic QueryWrapper<User> initPageWapper(User user) {QueryWrapper<User> qw= super.initPageWapper(user);if(!ObjectUtils.isEmpty(user.getName())){qw.like("name",user.getName());}if(!ObjectUtils.isEmpty(user.getAccount())){qw.like("account",user.getAccount());}if(!ObjectUtils.isEmpty(user.getPhone())){qw.like("phone",user.getPhone());}if(!ObjectUtils.isEmpty(user.getSex())){qw.eq("sex",user.getSex());}return qw;}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {QueryWrapper<User> qw = new QueryWrapper<>();qw.eq("account",username);User user = getBaseMapper().selectOne(qw);if(user==null){throw new UsernameNotFoundException("`${username}`不存在!");}if(user.getRoles()!=null){user.getRoles().forEach(role->{role.setMenus(menuDao.selectAllByRoleId(role.getId()));});}return user;}@Override@Transactional(rollbackFor = Exception.class)public void updatePwd(String oldpwd, String newpwd) throws Exception {User user = UserUtils.getUser();if(!passwordEncoder.matches(oldpwd,user.getPassword())){throw new Exception("旧密码不正确!");}UpdateWrapper<User> uw = new UpdateWrapper<>();uw.set("password",passwordEncoder.encode(newpwd));uw.eq("id",user.getId());getBaseMapper().update(uw);}@Override@Transactional(rollbackFor = Exception.class)public void resetPwd(User user, String newpwd) {UpdateWrapper<User> uw = new UpdateWrapper<>();uw.set("password",passwordEncoder.encode(newpwd));uw.eq("id",user.getId());getBaseMapper().update(uw);}@Override@Transactional(rollbackFor = Exception.class)public void updateInfo(User t) {UpdateWrapper<User> uw = new UpdateWrapper<>();uw.set("name",t.getName());uw.set("sex",t.getSex());uw.set("phone",t.getPhone());uw.set("header",t.getHeader());uw.eq("id",UserUtils.getUser().getId());getBaseMapper().update(uw);}@Overridepublic User getByOpenId(String openid) {QueryWrapper<User> qw = new QueryWrapper<>();qw.eq("openid",openid);return getBaseMapper().selectOne(qw);}@Override@Transactional(rollbackFor = Exception.class)public void register(User t) {UpdateWrapper<User> uw = new UpdateWrapper<>();uw.set("account",t.getPhone());uw.set("name",t.getName());uw.set("sex",t.getSex());uw.set("phone",t.getPhone());uw.set("header",t.getHeader());uw.eq("id",UserUtils.getUser().getId());getBaseMapper().update(uw);}@Overridepublic User loadUserByPhone(String phone) {QueryWrapper<User> qw = new QueryWrapper<>();qw.eq("phone",phone);return getBaseMapper().selectOne(qw);}@Overridepublic long countByRole(long roleId) {return getBaseMapper().countByRole(roleId);}@Overridepublic Long countByDayAndRole(Date day, long roleId) {return getBaseMapper().countByDayAndRole(day,roleId);}
}
  1. SecurityConfig
package com.junjunjun.aiwork;import com.fasterxml.jackson.databind.ObjectMapper;
import com.junjunjun.aiwork.security.LoginFailed;
import com.junjunjun.aiwork.security.LoginSuccess;
import com.junjunjun.aiwork.security.LogoutSuccess;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;@Configuration
@EnableMethodSecurity
@EnableWebSecurity
public class SecurityConfig {@Autowiredprivate LogoutSuccess logoutSuccess;@Autowiredprivate LoginSuccess loginSuccess;@Autowiredprivate LoginFailed loginFailed;@Value("${jwt.public.key}")private RSAPublicKey key;@Value("${jwt.private.key}")private RSAPrivateKey priv;/*** 获取AuthenticationManager(认证管理器),登录时认证使用*/@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {return authenticationConfiguration.getAuthenticationManager();}@Beanpublic PasswordEncoder passwordEncoder() {return PasswordEncoderFactories.createDelegatingPasswordEncoder();}
//    @Bean
//    public LocalValidatorFactoryBean validator() {
//        return new LocalValidatorFactoryBean();
//    }@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.csrf((csrf) -> csrf.ignoringRequestMatchers("/login/**")).httpBasic(Customizer.withDefaults()).rememberMe(Customizer.withDefaults())
//                //一次性校驗,比如找回密碼、註冊等驗證碼的
//            .oneTimeTokenLogin(Customizer.withDefaults()).authorizeHttpRequests(authorize -> authorize.requestMatchers("/static/**").permitAll().requestMatchers("/login/**","/api/wx/**").permitAll().anyRequest().authenticated()).formLogin(formLogin ->formLogin.successHandler(loginSuccess).failureHandler(loginFailed).permitAll()).logout(logOut->{logOut.logoutSuccessHandler(logoutSuccess).clearAuthentication(true).logoutRequestMatcher(new AntPathRequestMatcher("/logout")).deleteCookies("Authcation");}).oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())).sessionManagement(session ->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)).exceptionHandling((exceptions) -> exceptions.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint()).accessDeniedHandler(new BearerTokenAccessDeniedHandler()));return http.build();}@BeanJwtDecoder jwtDecoder() {return NimbusJwtDecoder.withPublicKey(this.key).build();}@BeanJwtEncoder jwtEncoder() {JWK jwk = new RSAKey.Builder(this.key).privateKey(this.priv).build();JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));return new NimbusJwtEncoder(jwks);}}
  1. UserUtils
package com.junjunjun.aiwork;import com.junjunjun.aiwork.entity.user.User;
import com.junjunjun.aiwork.service.user.IUserService;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;/*** 获取当前登录用户信息工具类*/
public final class UserUtils {/*** 获取当前登录用户* @return*/public static User getUser(){if(SecurityContextHolder.getContext()==null||SecurityContextHolder.getContext().getAuthentication()==null||!SecurityContextHolder.getContext().getAuthentication().isAuthenticated()){return null;}String username =  SecurityContextHolder.getContext().getAuthentication().getName();IUserService userService =SpringContextUtils.getBean(IUserService.class);try{return (User) userService.loadUserByUsername(username);} catch (UsernameNotFoundException e) {
//            throw new RuntimeException(e);return null;}}
}
  1. LoginFailed
package com.junjunjun.aiwork.security;import com.fasterxml.jackson.databind.ObjectMapper;
import com.junjunjun.aiwork.vo.Result;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;import java.io.IOException;
import java.nio.charset.StandardCharsets;@Component
public class LoginFailed implements AuthenticationFailureHandler {@Autowiredprivate ObjectMapper objectMapper;@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {Result<String> result = new Result<>();result.setCode(HttpStatus.UNAUTHORIZED.value()).setMsg("登录失败,"+exception.getMessage());response.setStatus(HttpStatus.UNAUTHORIZED.value());response.getOutputStream().write(objectMapper.writeValueAsString(result).getBytes(StandardCharsets.UTF_8));response.getOutputStream().flush();response.getOutputStream().close();}
}
  1. LogoutSuccess
package com.junjunjun.aiwork.security;import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;import java.io.IOException;@Component
public class LogoutSuccess implements LogoutSuccessHandler {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {}
}
  1. LoginSuccess
package com.junjunjun.aiwork.security;import com.junjunjun.aiwork.Log;
import com.junjunjun.aiwork.SpringContextUtils;
import com.junjunjun.aiwork.UserUtils;
import com.junjunjun.aiwork.entity.user.User;
import com.junjunjun.aiwork.service.system.ILogService;
import com.junjunjun.aiwork.service.user.IUserService;
import com.junjunjun.aiwork.vo.Result;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Arrays;
import java.util.Date;
import java.util.stream.Collectors;@Component
public class LoginSuccess implements AuthenticationSuccessHandler {
//jwt的编码工具private JwtEncoder encoder;@Autowiredprivate ILogService logService;@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//        Result<String> result = new Result<>();
//        result.setCode(401).setMsg("登录失败,"+exception.getMessage());
//
//        response.getOutputStream().write(objectMapper.writeValueAsString(result).getBytes(StandardCharsets.UTF_8));
//        response.getOutputStream().flush();
//        response.getOutputStream().close();if(encoder==null){encoder = SpringContextUtils.getBean(JwtEncoder.class);}Instant now = Instant.now();long expiry = 36000L;// @formatter:offString scope = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(" "));System.out.println(scope);JwtClaimsSet claims = JwtClaimsSet.builder().issuer("self").issuedAt(now).expiresAt(now.plusSeconds(expiry)).subject(authentication.getName()).claim("scope", scope).build();// @formatter:onString token  = this.encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();response.addHeader("Authorization",token);//记录用户登录日志com.junjunjun.aiwork.entity.system.Log log = new com.junjunjun.aiwork.entity.system.Log();log.setDate(new Date());log.setMsg(authentication.getName()+"登录系统");
//        log.setParam(Arrays.toString(joinPoint.getArgs()));
//            joinPoint.getTarget().getClass().getAnnotation(Log.class);
//        log.setTarget(joinPoint.getTarget().toString());log.setType("登录");log.setUsername(authentication.getName());log.setTitle("登录成功");// 获取请求相关参数log.setRequestmethod(request.getMethod());log.setUrl(request.getRequestURI());log.setIp(request.getRemoteAddr());// 保存日志数据logService.save(log);}
}
  1. app.yml
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDnHWwRNhe9aEGW
6Ov3Asou+aNjzTwODG6kiWiH6FLKIeZRMmI93tc9cLjMJR7qD4hoQlmvRhOhLStz
equ7NWKofR5dmXAJRlVbmslj9s8QjFfJpVS3smvNUIzxspjVVRVU4pMk/oTiImU8
vPA0kETUnEJCm9nusXy8nFVg3FXz5qNAeBshv4gJIJ51sbo0LQFWLPgEIORhTqia
sRSRYdpKqtxbO0NC04vL/ij+M2P9fpeAznPUQ9RDWZsS0n6wdX4pqlRe8hOE7wsk
8wJ2pBFRQZrutLsgxG1hKjxG8+nolTIF2JOuKU4rD6FAzQGhN+pM2Td8SPUgzYFF
6JwEuYe3AgMBAAECggEAcAIQZ98BbmhisVK9WX9eoZmXGD1VR7G6xb9ai9OKnDAF
wYBboISpaDuDNF0hOGrCv1S4xnt98/VCQ7tGE3ryD5pj5xT/+V6+66B+zhXFkM2T
lkSsZELygBTz2gemjm2n4vXUPm+j0zy8N82R1Me+tBPvAP0rh8U5IOfbcRAYEBXh
RuxwrS8GrRY0XdGNwwxgXYYZoipUBf5mzr0OPtcH6dVkFFZOwcoiJfK8YUgO7/sN
SiB61nVfC+Xr+DffFXh4DrPBJh0wwvW+EC/quxsx0rvvhF55Ix+3J78rBjphzoo/
fSNsWsy+msu/bVo1qtDYt2SU1REJDSxKHvvwAaqowQKBgQD8Sz9xeGeRwxR+f+U0
I+DGw/eMhv76IxKorkusp2ewD+dpdAZvBS6riC5RpbncmRsRqeAJcF33ri37UlYM
HrJ5qvKf3XzX2qVvD9+HfQT6DMwiN6Kf7pwEiW9izPGnClC8Sp+NtVS/Cl3xVPyf
qkMAojxq3Olrreu9D4d1cByfkQKBgQDqgofUrCJ+UxjtrpkWp3wwsMCKlPlfOPnS
Mr6SvtBgSefZJ2BkbrSwraXg43DWp0EQei3ncNcqD4+ZxTHFC0byUYTZGVIZy8v2
iBd4gCBda/h98537hgn08ewOrVbfzdc/VOcGiM3oCFmnmCu503C/4btovx1EX5NZ
T6eu8R6exwKBgQCXOrPI3JeYdAsw2JEXc/okqwsUHUdxwNxKaE/MKnyouJ/KQlYo
7j5Pa833+xMx/ESRjfZRMh3hfXLqlywlD7GD5MiSsz1GVjWcITOoD3SQPmOOjzLY
EztKLOernZWsvy4t7708QFmQOjKdmofjXTfUhWsZ01nHxfTs2wYjixRdQQKBgELS
IsYUrld0chv3riNoYdi15IcYX94kK/sa/nZt7I4jQO79a1fyu4XEVQm5hQOE8OMz
DTVvT7/qYy6bo8zTo8mUNNhvgJddHKcIrDdnutEj3NB2xENRNDEZZfvgpGptCrI/
DP0tqg49ot5AW+U9PFlQhsgX/MzotuGx8sPEbMctAoGBAMDOByeZfqB2W0BMgwj4
RnHccAdwajFADaW4wnNJDJKA+BxwfxJEv4nQeRqYM42OmPih0A0SQcsuvyjj/mWH
RtN2I8T2bW5eGKhG5SIvNtnRhBPNul9hCqGlfyA6bJuk96hRBk9jD78kZzpWVC02
3XeBd77Zz8gIo/TXGsf6duMN
-----END PRIVATE KEY-----
  1. app.pub
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5x1sETYXvWhBlujr9wLK
LvmjY808DgxupIloh+hSyiHmUTJiPd7XPXC4zCUe6g+IaEJZr0YToS0rc3qruzVi
qH0eXZlwCUZVW5rJY/bPEIxXyaVUt7JrzVCM8bKY1VUVVOKTJP6E4iJlPLzwNJBE
1JxCQpvZ7rF8vJxVYNxV8+ajQHgbIb+ICSCedbG6NC0BViz4BCDkYU6omrEUkWHa
SqrcWztDQtOLy/4o/jNj/X6XgM5z1EPUQ1mbEtJ+sHV+KapUXvIThO8LJPMCdqQR
UUGa7rS7IMRtYSo8RvPp6JUyBdiTrilOKw+hQM0BoTfqTNk3fEj1IM2BReicBLmH
twIDAQAB
-----END PUBLIC KEY-----

1.2 权限校验实现

  使用注解@EnableMethodSecurity开启方法级的权限校验,并在接口上使用@PreAuthorize进行权限校验,定义AuthService实现权限的自定义校验。

  1. BaseController
package com.junjunjun.aiwork.api;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.junjunjun.aiwork.entity.BaseEntity;
import com.junjunjun.aiwork.entity.user.User;
import com.junjunjun.aiwork.service.IBaseService;
import com.junjunjun.aiwork.vo.PageInfo;
import com.junjunjun.aiwork.vo.Result;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;/*** 接口基础类*/
@Slf4j
public  abstract class BaseController<T extends BaseEntity,S extends IBaseService<T>> {@Autowiredprotected S service;//@Validated @NotNull(message="id不能为空")@GetMapping("/get/{id}")@ResponseBody@PreAuthorize("hasAuthority('SCOPE_ROLE_ADMIN') or (@authService.checkGet(#root,#id) and hasAuthority('SCOPE_'+#this.this.class.name+':read'))")public Result<T> get( @PathVariable("id") Long id){log.info("请求用户数据`${id}`");T t = service.getById(id);return new Result<T>(200,"",t);}@PostMapping("/page")@ResponseBody@PreAuthorize("hasAuthority('SCOPE_ROLE_ADMIN') or (hasAuthority('SCOPE_'+#this.this.class.name+':read') and @authService.checkPage(#root,#page))")public Result<Page<T>> page(@RequestBody @Validated PageInfo<T> page){log.info("请求用户分页数据`${page}`");Page<T> pages = service.page(page.getPage(),service.initPageWapper(page.getT()));return new Result<Page<T>>(200,"",pages);}@PostMapping("/save")@ResponseBody@PreAuthorize("hasAuthority('SCOPE_ROLE_ADMIN') or (hasAuthority('SCOPE_'+#this.this.class.name+':create') and @authService.checkSave(#root,#t))")public Result<T> save(@RequestBody @Validated T t, HttpServletResponse response){log.info("请求保存用户数据`${t}`");try{boolean result = service.save(t);return new Result<T>(200,"",t);} catch (Exception e) {log.error("保存数据异常",e);response.setStatus(500);return new Result<T>(500,e.getMessage(),t);}}@PostMapping("/update")@ResponseBody@PreAuthorize("hasAuthority('SCOPE_ROLE_ADMIN') or (hasAuthority('SCOPE_'+#this.this.class.name+':write') and @authService.checkUpdate(#root,#t))")public Result<T> update(@RequestBody @Validated T t, HttpServletResponse response){log.info("请求更新用户数据`${y}`");try{boolean result = service.update(t);return new Result<T>(200,"",t);} catch (Exception e) {log.error("更新数据异常",e);response.setStatus(500);return new Result<T>(500,e.getMessage(),null);}}//@Validated @NotNull(message="id不能为空")@GetMapping("/delete/{id}")@ResponseBody@PreAuthorize("hasAuthority('SCOPE_ROLE_ADMIN') or (hasAuthority('SCOPE_'+#this.this.class.name+':delete') and @authService.checkDelete(#root,#id))")public Result<Boolean> delete(@PathVariable  Long id, HttpServletResponse response){log.info("请求删除用户数据`${id}`");try{service.removeById(id);return new Result<Boolean>(200,"",true);} catch (Exception e) {log.error("删除数据异常",e);response.setStatus(500);return new Result<Boolean>(500,e.getMessage(),false);}}
}
  1. AuthService
package com.junjunjun.aiwork.service;import com.junjunjun.aiwork.entity.BaseEntity;
import com.junjunjun.aiwork.vo.BaseVo;
import com.junjunjun.aiwork.vo.PageInfo;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.method.MethodInvocationResult;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;/*** 动态监测当前登录用户是否有某接口的操作权限*/
@Component
public class AuthService  {/*** 检测get接口是否具有权限* @param operations* @return*/public boolean checkGet(MethodSecurityExpressionOperations operations,Long id) {// ... authorization logicreturn true;}/*** 分页权限限制* @param operations* @param id* @return* @param <T>*/public <T extends BaseEntity> boolean checkPage(MethodSecurityExpressionOperations operations, PageInfo<T> id) {// ... authorization logicreturn true;}/*** 数据保存权限限制* @param operations* @param id* @return* @param <T>*/public <T extends BaseEntity> boolean checkSave(MethodSecurityExpressionOperations operations,T id) {// ... authorization logicreturn true;}/*** 数据更新权限限制* @param operations* @param t* @return* @param <T>*/public <T extends BaseEntity> boolean checkUpdate(MethodSecurityExpressionOperations operations, T t) {// ... authorization logicreturn true;}/*** 数据删除权限限制* @param operations* @param id* @return*/public boolean checkDelete(MethodSecurityExpressionOperations operations,Long id) {// ... authorization logicreturn true;}
}

二. 操作日志功能实现

  定义自定义注解,通过AOP实现操作日志的记录功能。

  1. Log
package com.junjunjun.aiwork;import java.lang.annotation.*;@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {public String title() ;                                // 模块名称public String type() ;     // 业务类型(0其它 1新增 2修改 3删除)public String msg() ;     // 业务类型(0其它 1新增 2修改 3删除)
}
  1. LoggingAspect
package com.junjunjun.aiwork;import com.junjunjun.aiwork.entity.user.User;
import com.junjunjun.aiwork.service.system.ILogService;
import com.junjunjun.aiwork.service.system.impl.LogService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;/*** aop实现系统操作日志的处理*/
@Aspect
@Component
@Slf4j
public class LoggingAspect {@Autowiredprivate ILogService logService;@Pointcut("@annotation(com.junjunjun.aiwork.Log)")public void saveLog() {}
//    @After("execution(* com.junjunjun.aiwork.service.**.save*(..)) || execution(* com.junjunjun.aiwork.service.**.update*(..) || execution(* com.junjunjun.aiwork.service.**.delete*(..))")@After(value = "saveLog()")public void doAroundAdvice(JoinPoint joinPoint) {try {if (LogService.class.getName().equals(joinPoint.getTarget().getClass().getName())) {return ;}MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();Log syslog = methodSignature.getMethod().getAnnotation(Log.class);com.junjunjun.aiwork.entity.system.Log log = new com.junjunjun.aiwork.entity.system.Log();log.setDate(new Date());log.setMsg(syslog.msg());log.setParam(Arrays.toString(joinPoint.getArgs()));
//            joinPoint.getTarget().getClass().getAnnotation(Log.class);log.setTarget(joinPoint.getTarget().toString());log.setType(syslog.type());User user = UserUtils.getUser();if (user != null) {log.setUsername(user.getUsername());}Method method = methodSignature.getMethod();log.setMethod(method.getName());log.setTitle(syslog.title());// 获取请求相关参数ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();HttpServletRequest request = requestAttributes.getRequest();log.setRequestmethod(request.getMethod());log.setUrl(request.getRequestURI());log.setIp(request.getRemoteAddr());// 保存日志数据logService.save(log);} catch (Throwable e) {throw new RuntimeException(e);}}
}
  1. BaseService
package com.junjunjun.aiwork.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.junjunjun.aiwork.Log;
import com.junjunjun.aiwork.dao.BaseDao;
import com.junjunjun.aiwork.entity.BaseEntity;
import com.junjunjun.aiwork.entity.user.User;
import com.junjunjun.aiwork.service.IBaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;import java.io.Serializable;@Slf4j
public  abstract class BaseService<T extends BaseEntity,M extends BaseDao<T>> extends ServiceImpl<M,T> implements IBaseService<T> {@Override@Log(title = "更新数据",type="更新",msg="更新数据${t}")@Transactional(rollbackFor = Exception.class)public boolean update(T t) {log.info("更新数据${t}");UpdateWrapper<T> uw =  initUpdateWrapper(t);uw.eq("id",t.getId());return getBaseMapper().update(uw)>0;}@Override@Log(title = "保存数据",type="保存",msg="保存数据${entity}")@Transactional(rollbackFor = Exception.class)public boolean save(T entity) {return super.save(entity);}@Override@Log(title = "删除数据",type="删除",msg="删除数据${id}")@Transactional(rollbackFor = Exception.class)public boolean removeById(Serializable id) {return super.removeById(id);}/*** 子类实现具体的修改的数据字段* @param t* @return*/protected  UpdateWrapper<T> initUpdateWrapper(T t) {return new UpdateWrapper<>();}@Overridepublic QueryWrapper<T> initPageWapper(T t) {QueryWrapper<T> queryWrapper = new QueryWrapper<>();//TODO 在子类中实现对应的搜索参数的限制return queryWrapper;}
}

三. 参数合法性校验功能实现

  使用jakarta.validation实现接口的参数合法性校验,接口上使用@Validated开启参数校验,entity上使用@NotBlank/ @NotEmpty等实现需要校验的字段,一下以user实体为例演示参数校验功能。

  1. Create
package com.junjunjun.aiwork.validate;import jakarta.validation.groups.Default;/*** 创建数据分组*/
public interface Create extends Default {
}
  1. Update
package com.junjunjun.aiwork.validate;import jakarta.validation.groups.Default;/*** 更新数据分组*/
public interface Update extends Default {
}
  1. User
package com.junjunjun.aiwork.entity.user;import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.junjunjun.aiwork.entity.BaseEntity;
import com.junjunjun.aiwork.entity.system.Role;
import com.junjunjun.aiwork.validate.Create;
import com.junjunjun.aiwork.validate.Update;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.*;
import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;/*** 用户表*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Accessors(chain = true)
@EqualsAndHashCode( callSuper =true)
@TableName(value="junjunjun_user",resultMap="userResultMap")
public class User extends BaseEntity implements UserDetails {/*** 账号*/@NotBlank(message = "账号信息不能为空",groups = {Update.class, Create.class})private String account;/*** 姓名*/@NotBlank(message = "姓名信息不能为空",groups = {Update.class, Create.class})private String name;/*** 头像*/private String header;/*** 性别*/private String sex;/*** 电话*/private String phone;/*** 密码*/@NotBlank(message = "密码不能为空",groups = { Create.class})private String password;/*** 小程序的openid*/private String openid;/*** 角色数据*/@TableField(exist = false)@NotEmpty(message = "角色信息不能为空",groups = {Update.class, Create.class})private List<Role> roles;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {List<GrantedAuthority> auths = new ArrayList<>();if(roles!=null){roles.forEach(item->{auths.add(item);if(item.getMenus()!=null){item.getMenus().forEach(menuItem->{auths.add(menuItem);});}});}return auths;}@Overridepublic String getUsername() {return account;}
}
  1. BaseController
package com.junjunjun.aiwork.api;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.junjunjun.aiwork.entity.BaseEntity;
import com.junjunjun.aiwork.entity.user.User;
import com.junjunjun.aiwork.service.IBaseService;
import com.junjunjun.aiwork.vo.PageInfo;
import com.junjunjun.aiwork.vo.Result;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;/*** 接口基础类*/
@Slf4j
public  abstract class BaseController<T extends BaseEntity,S extends IBaseService<T>> {@Autowiredprotected S service;//@Validated @NotNull(message="id不能为空")@GetMapping("/get/{id}")@ResponseBody@PreAuthorize("hasAuthority('SCOPE_ROLE_ADMIN') or (@authService.checkGet(#root,#id) and hasAuthority('SCOPE_'+#this.this.class.name+':read'))")public Result<T> get( @PathVariable("id") Long id){log.info("请求用户数据`${id}`");T t = service.getById(id);return new Result<T>(200,"",t);}@PostMapping("/page")@ResponseBody@PreAuthorize("hasAuthority('SCOPE_ROLE_ADMIN') or (hasAuthority('SCOPE_'+#this.this.class.name+':read') and @authService.checkPage(#root,#page))")public Result<Page<T>> page(@RequestBody @Validated PageInfo<T> page){log.info("请求用户分页数据`${page}`");Page<T> pages = service.page(page.getPage(),service.initPageWapper(page.getT()));return new Result<Page<T>>(200,"",pages);}@PostMapping("/save")@ResponseBody@PreAuthorize("hasAuthority('SCOPE_ROLE_ADMIN') or (hasAuthority('SCOPE_'+#this.this.class.name+':create') and @authService.checkSave(#root,#t))")public Result<T> save(@RequestBody @Validated T t, HttpServletResponse response){log.info("请求保存用户数据`${t}`");try{boolean result = service.save(t);return new Result<T>(200,"",t);} catch (Exception e) {log.error("保存数据异常",e);response.setStatus(500);return new Result<T>(500,e.getMessage(),t);}}@PostMapping("/update")@ResponseBody@PreAuthorize("hasAuthority('SCOPE_ROLE_ADMIN') or (hasAuthority('SCOPE_'+#this.this.class.name+':write') and @authService.checkUpdate(#root,#t))")public Result<T> update(@RequestBody @Validated T t, HttpServletResponse response){log.info("请求更新用户数据`${y}`");try{boolean result = service.update(t);return new Result<T>(200,"",t);} catch (Exception e) {log.error("更新数据异常",e);response.setStatus(500);return new Result<T>(500,e.getMessage(),null);}}//@Validated @NotNull(message="id不能为空")@GetMapping("/delete/{id}")@ResponseBody@PreAuthorize("hasAuthority('SCOPE_ROLE_ADMIN') or (hasAuthority('SCOPE_'+#this.this.class.name+':delete') and @authService.checkDelete(#root,#id))")public Result<Boolean> delete(@PathVariable  Long id, HttpServletResponse response){log.info("请求删除用户数据`${id}`");try{service.removeById(id);return new Result<Boolean>(200,"",true);} catch (Exception e) {log.error("删除数据异常",e);response.setStatus(500);return new Result<Boolean>(500,e.getMessage(),false);}}
}

文章转载自:
http://beige.isnyv.cn
http://bakshish.isnyv.cn
http://bucuresti.isnyv.cn
http://adriatic.isnyv.cn
http://aigret.isnyv.cn
http://arbitrarily.isnyv.cn
http://agreeable.isnyv.cn
http://actinouranium.isnyv.cn
http://addend.isnyv.cn
http://alular.isnyv.cn
http://cartographer.isnyv.cn
http://accolade.isnyv.cn
http://cacophony.isnyv.cn
http://alcor.isnyv.cn
http://borecole.isnyv.cn
http://carnivore.isnyv.cn
http://aril.isnyv.cn
http://candor.isnyv.cn
http://boogiewoogie.isnyv.cn
http://bradshaw.isnyv.cn
http://cation.isnyv.cn
http://caracal.isnyv.cn
http://abysm.isnyv.cn
http://bathing.isnyv.cn
http://actium.isnyv.cn
http://ccitt.isnyv.cn
http://animadversion.isnyv.cn
http://cathomycin.isnyv.cn
http://chorine.isnyv.cn
http://angiocarp.isnyv.cn
http://www.dtcms.com/a/281714.html

相关文章:

  • Ubuntu 22.04 安装 mysql-server与 postgreSQL 服务端
  • el-tooltip 快速滚动的时候出现残影如何解决 vue3
  • 30.安卓逆向2-frida hook技术-hook So文件(获取app加载的所有模块、导入函数、导出函数、hook SO函数)
  • 区块链发展史全景长图
  • 强化学习Reinforcement Learning
  • 基于Python的图像文字识别系统
  • Glide双内存缓存加载机制详细介绍
  • K型热电偶电动势以及温度对照表
  • 驱动开发系列61- Vulkan 驱动实现-SPIRV到HW指令的实现过程(2)
  • [Science]论文 视黄素与细胞修复
  • 2025-07-15通过边缘线检测图像里的主体有没有出血
  • IMU 能为无人机提供什么数据?
  • 【论文阅读】A Survey on Knowledge-Oriented Retrieval-Augmented Generation(3)
  • 移动端字体适配
  • 谷歌浏览器控制台如何切换中文
  • StampedLock分析
  • 链表的 哑结点的本质
  • Python 程序设计讲义(1):PyCharm 安装教程
  • WebView JSBridge 无响应问题排查实录 全流程定位桥接调用失效
  • 深度学习·目标检测和语义分割基础
  • 77、【OS】【Nuttx】【启动】caller-saved 和 callee-saved 示例:栈指针和帧指针(上)
  • Qt图形视图框架5-状态机框架
  • Springboot儿童认知图文辅助系统6yhkv(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 再见吧,Windows自带记事本,这个轻量级文本编辑器太香了
  • 基于mybatis的基础操作的思路
  • C++-linux系统编程 8.进程(二)exec函数族详解
  • 终端安全管理系统为什么需要使用,企业需要的桌面管理软件
  • X 射线探伤证考试核心:辐射安全基础知识点梳理
  • golang二级缓存示例
  • HC165并转串