当前位置: 首页 > 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://www.lryc.cn/news/589666.html

相关文章:

  • 从抽象函数到可计算导数 ——SymPy 中占位、求导、代入的完整闭环
  • OpenAI GPT-4o技术详解:全能多模态模型的架构革新与生态影响
  • AJAX 开发中的注意点
  • cursor使用mcp连接mysql数据库,url方式
  • Python 程序设计讲义(1):PyCharm 安装教程
  • 网络基础10 业务访问控制--ACL与包过滤
  • HarmonyOS-ArkUI: Web组件加载流程1
  • 隐私计算四大主流开源框架:从学术研究到工业落地,附PySyft实战Demo
  • 【PTA数据结构 | C语言版】根据层序序列重构二叉树
  • 【PTA数据结构 | C语言版】前序遍历二叉树
  • 【UniApp】Vue2 scss 预编译器默认已由 node-sass 更换为 dart-sass
  • 快速了解 HTTPS
  • 使用JS编写动态表格
  • ES2023 新特性解析_数组与对象的现代化操作指南
  • ffmpeg音视频处理大纲
  • 【删库跑路】一次删除pip的所有第三方库
  • Python语法入门之装饰器的基本用法
  • 21-C#的委托简单使用-1
  • 移动碰撞法 ——套料排版算法——CAD c#
  • 一文读懂循环神经网络—门控循环单元
  • Agentic AI 的威胁与缓解措施
  • 李白周游记50篇
  • MySQL锁机制与SQL优化详解
  • 学习C++、QT---26(QT中实现记事本项目实现文件路径的提示、C++类模板、记事本的行高亮的操作的讲解)
  • 应用部署作业-02-流程
  • C++-linux系统编程 8.进程(二)exec函数族详解
  • Qt .pro中的.pri详解(四)
  • 【Trea】Trea国际版|海外版下载
  • 【NBA】75 Greatest NBA Players of All Time
  • 【Android】日志的使用