springboot项目中集成shiro+jwt完整实例代码

2025-08-07 21:50

本文主要是介绍springboot项目中集成shiro+jwt完整实例代码,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《springboot项目中集成shiro+jwt完整实例代码》本文详细介绍如何在项目中集成Shiro和JWT,实现用户登录校验、token携带及接口权限管理,涉及自定义Realm、ModularRe...

简介

现在主流的安全框架分别为Shiro和Spring Security。关于两者之间的优缺点不是本文的重点,有兴趣的可以在网上搜搜,各种文章也都分析的很清楚。那么简单来说,Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能。(不一定要建立所谓的五张表,我们要做到控制自如的使用

目的

通过集成shiro,jwt我们要实现:用户登录的校验;登录成功后返回成功并携带具有身份信息的token以便后续调用接口的时候做认证;对项目的接口进行权限的限定等。

需要的jar

本文使用的gradel作为jar包管理工具,maven也是使用相同的jar

//shiro的jar
implementation 'org.apache.shiro:shiro-spring:1.7.1'
//jwt的jar
implementation 'com.auth0:java-jwt:3.15.0'
implementation 'com.alibaba:fastjson:1.2.76'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

集成过程

1.配置shiro

@Configuration
public class ShiroConfig {
    /*
     * 解决spring aop和注解配置一起使用的bug。如果您在使用shiro注解配置的同时,引入了spring
     * aop的starter,会有一个奇怪的问题,导致shiro注解的请求,不能被映射 
     */  
    @Bean
    public static DefaultAdvisorAutoProxyCreator creator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }
    /**
     * Enable Shiro AOP annotation support. --<1>
     *
     * @param securityManager Security Manager
     * @return AuthorizationAttributeSourceAdvisor
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    /**
     * Use for login password matcher --<2>
     *
     * @return HashedCredentialsMatcher
     */
    @Bean("hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        // set name of hash
        matcher.setHashAlgorithmName("SHA-256");
        // Storage format is hexadecimal
        matcher.setStoredCredentialsHexEncoded(true);
        return matcher;
    }
    /**
     * Realm for login --<3>
     *
     * @param matcher password matcher
     * @return PasswordRealm
     */
    @Bean
    public LoginRealm loginRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {
        LoginRealm loginRealm = new LoginRealm(LOGIN);
        loginRealm.setCredentialsMatcher(matcher);
        return loginRealm;
    }
    /**
     * JwtReal, use for token validation --<4>
     *
     * @return JwtRealm
     */
    @Bean
    public JwtRealm jwtRealm() {
        return new JwtRealm(JWT);
    }
    //  --<5>
    @Bean
    public OurModularRealmAuthenticator userModularRealmAuthenticator() {
        // rewrite ModularRealmAuthenticator
        DataAuthModularRealmAuthenticator modularRealmAuthenticator = new DataAuthModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        return modularRealmAuthenticator;
    }
    // --<6>
    @Bean(name = "securityManager")
    public SecurityManager securityManager(
            @Qualifier("userModularRealmAuthenticator") OurModularRealmAuthenticatormodular,
            @Qualifier("jwtRealm") JwtRealm jwtRealm,
            @Qualifier("loginRealm") LoginRealm loginRealm
    ) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        // set realm
        manager.setAuthenticator(modular);
        // set to use own realm
        List<Realm> realms = new ArrayList<>();
        realms.add(loginRealm);
        realms.add(jwtRealm);
        manager.setRealms(realms);
        // close Shiro's built-in session
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaLuator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        manager.setSubjectDAO(subjectDAO);
        return manager;
    }
    // --<7>
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, Filter> filter = new LinkedHashMap<>(1);
        filter.put("jwt", new JwtFilter());
        shiroFilterFactoryBean.setFilters(filter);
        Map<String, String> filterMap = new HashMap<>();
        filterMap.put("/login/**", "anon");
        filterMap.put("/v1/**", "jwt");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        return shiroFilterFactoryBean;
    }
}
  • 开启shiro注解支持,具体原理请参考springboot中shiro使用自定义注解屏蔽接口鉴权实现
  • 配置shiro登录验证的密码加密方式:Shiro 提供了用于加密密码验证密码服务的 CredentialsMatcher 接口,HashedCredentialsMatcher 正是 CredentialsMatcher 的一个实现类。
  • LoginRealm:自定义的Realm,用于处理用户登录验证的Realm,在shiro中验证及授权等信息会在Realm中配置,详细解释请参考shiro简介
  • JwtRealm:自定义的Realm,用户在登录后访问服务时做token的校验,用户权限的校验等。
  • 配置DataAuthModularRealmAuthenticator:是在项目中存在多个Realm时,根据项目的认证策略可以选择匹配需要的Realm。
  • SecurityManager:Shiro的核心组件,管理着认证、授权、会话管理等,在这里我把所有的自定义的Realm等资源加入到SecurityManager中
  • Shiro的过滤器:定制项目的path过滤规则,并将我们自定义的Filter加入到Shiro中的shiroFilterFactoryBean中

2.创建自定义Realm

2.1 LoginRealm用于处理用户登录

public class LoginRealm extends AuthorizingRealm {
    public LoginRealm(String name) {
        setName(name);
    }
    // 获取user相关信息的service类
    @Autowired
    private UserLoginService userLoginService;
    // supports方法必须重写,这是shiro处理流程中的一部分,他会通过此方法判断realm是否匹配的正确
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof LoginDataAutoToken;
    }
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        LoginDataAutoToken token = (LoginDataAutoToken) auth;
        serviceLog.info(token.getUsername() + "password auth start...");
        User user = userLoginService.selectUserByName(token.getUsername());
        if (user == null) throw new UnknownAccountException();
        Object credentials = user.getPassword();
        // save username and role to Attribute
        ServletUtils.userNameRoleTo.accept(usChina编程er.getUserName(), (int) user.getUserType());
        return new SimpleAuthenticationInfo(user, credentials, super.getName());
    }
}

2.2  JwtRealm用于在登录之后,用户的token是否正确以及给当前用户授权等

public class JwtRealm extends AuthorizingRealm {
    public JwtRealm(String name) {
        setName(name);
    }
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtDataAutoToken;
    }
    // 给当前用户授权,只有在访问的接口上配置了shiro的权限相关的注解的时候才会进入此方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        UserEnum.Type userEnum = EnumValue.dataValueOf(
                UserEnum.Type.class,
                ServletUtils.userNameRoleFrom.get().getUserRole()
        );
        Set<String> roles = new HashSet<>();
        roles.add(userEnum.getDesc());
        // 授权角色如果有其他的权限则都已此类的方式授权
        authorizationInfo.setRoles(roles);
        return authorizationInfo;
    }
    // 验证此次request携带的token是否正确,如果正确解析当前token,并存入上下文中
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        // verify token
        String token = (String) auth.getCredentials();
        TokenUtils.verify(token);
        TupleNameRole tupleNameRole = TokenUtils.tokenDecode(token);
        ServletUtils.userNameRoleTo.accept(tupleNameRole.getUsername(), tupleNameRole.getUserRole());
        return new SimpleAuthenticationInfo(token, token, ((JwtDataAutoToken) auth).getName());
    }
}

2.3 OurModularRealmAuthenticator用于匹配的相应的Realm

public class DataAuthModularRealmAuthenticator extends ModularRealmAuthenticator {
    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        assertRealmsConfigured();
        DataAutoToken dataAutoToken = (DataAutoToken) authenticationToken;
        Realm realm = getRealm(dataAutoToken);
        return DOSingleRealmAuthentication(realm, authenticationToken);
    }
    private Realm getRealm(DataAutoToken dataAutoToken) {
        for (Realm realm : getRealms()) {
            // 根据定义的realm的name和dataAutoToken的name匹配相应的realm 
            if (realm.getName().contains(dataAutoToken.getName())) {
                return realm;
            }
        }
        return null;
    }
}

2.4 DataAutoToken及实现类

DataAuthModularRealmAuthenticator的doSingleRealmAuthentication(realm, authenticationToken)做检验的时候需要两个参数,一个是Realm另一个是我们定义的储存验证信息的AuthenticationToken或者它的实现类。

DataAutoToken:

public interface DataAutoToken {
    String getName();
}

 LoginDataAutoToken :

public class LoginDataAutoToken extends UsernamePasswordToken implements DataAuthToken {
    public LoginDataAuthToken(final String username, final String password) {
        super(username, password);
    }
    @Override
    public String getName() {
        return LOGIN;
    }
}

 JwtDataAutoToken:

public class JwtDataAutoToken implements AuthenticationToken, DataAuthToken {
    private final String token;
    public JwtDataAuthToken(String token) {
        this.token = token;
    }
    @Override
    public Object getPrincipal() {
        return token;
    }
    @Override
    public Object getCredentials() {
        return token;
    }
    @Override
    public String getName() {
        return JWT;
    }
}

2.5 JwtFilter处理在shiro配置的自定义的Filter

此类用于处理不在登录下必须携带发行的Token访问接口,如果Token存在,则使用shiro subject做token的和访问权限的校验。

public class JwtFilter extends BasicHttpAuthenticationFilter {
    private final BiConsumer<ServletResponse, ErrorMessage> writeResponse = (response, message) ->
            Utils.renderString.accept(
                    (HttpServletResponse) response,
                    JSON.toJSONString(ResponseResult.fail(message), SerializerFeature.WriteMapNullValue)
            );
    /**
     * @param request     ServletRequeswww.chinasem.cnt
     * @param response    ServletResponse
     * @param mappedValue mappedValue
     * @return 是否成功
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        //input request to request log file
        requestLog.info(
                "path:{}, method:{}",
                httpServletRequest.getServletPath(),
                httpServletRequest.getMethod()
        );
        String token = httpServletRequest.getHeader(Constant.TOKEN);
        if (token != null) {
            return executeLogin(request, response);
        } else {
            writeResponse.accept(response, ErrorMessage.TOKEN_NOT_EXIST);
            return false;
        }
    }
    /**
     * execute login
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) {
        HttpSeeOvQmifmfDrvletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader(Constant.TOKEN);
        try {
            JwtDataAuthToken jwtToken = new JwtDataAuthToken(token);
            // validate user permission
            getSubject(request, response).login(jwtToken);
            return true;
        } catch (AuthenticationException e) {
            Throwable throwable = e.getCause();
            if (throwable instanceof TokenExpiredException) {
                writeResponse.accept(response, ErrorMessage.TOKEN_HAS_EXPIRED);
            } else {
                writeResponse.accept(response, ErrorMessage.TOKEN_INVALID);
            }
        }
        return false;
    }
    /**
     * support across domains
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-Control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }

2.6 controller层登录和其他接口

@RestController
public class AuthController {
    @Autowired
    private UserService userService;
    @PostMapping("/login")
    public ResponseResult<String> login(@RequestBody UserReqDto userReqDto) {
        userService.login(userLoginReqDto.getUsername(), userReqDto.getPassword());
        return ResponseResult.success();
    }
    // shiro角色注解,admin才可以访问此接口
    @RequiresRoles("admin")
    @PostMapping("/v1/user")
    public ResponseResult<String> addUser(@RequestBody UserAddReqDto userAddReqDto) {
        userService.add(userAddReqDto);
        return ResponseResult.success();
    }
    @PostMapping("/v1/token/verify")
    public ResponseResult<String> verify() {
        return ResponseResult.success(false);
    }
    @PostMapping("/v1/token/refresh")
    public ResponseResult<String> refresh() {
        return ResponseResult.success();
    }
}

2.7 service层

@Service
public class UserServiceImpl implements UserService {
    @Override
    public void login(String username, String password) {
        // Use shiro to verify the username and password
        Subject subject = SecurityUtils.getSubject();
        LoginDataAutoToken token = new LoginDataAutoToken(username, password);
        subject.login(token);
    }
    @Transactional
    @Override
    public void add(UserAddReqDto dto) {
        User user = getUserByName.apply(dto.getUsername());
        if (user != null) {
            throw new DataAuthException(ErrorMessage.USER_ALREADY_EXISTS);
        } else {
            User newUser = new User();
            // 设置user的信息
            post(newUser); // insert user to database
        }
}

2.8 jwt工具类

public final class TokenUtils {
    private TokenUtils() {
    }
    /**
     * @param username username
     * @param role     user role
     * @return The encrypted token
     */
    public static String createToken(String username, int role) {
        Date date = new Date(System.currentTimeMillis() + Constant.TOKEN_EXPIRE_TIME);
        Algorithm algorithm = Algorithm.HMAC256(username);
        return JWT.create()
                .withClaim(Constant.USER_NAME, username)
                .withClaim(Constant.USER_ROLE, role)
                .withExpiresAt(date)
                .sign(algorithm);
    }
    /**
     * @param username username
     * @param role     user role
     * @return The encrypted token
     */
    public static String refreshToken(String username, int role) {
        return createToken(username, role);
    }
    /**
     * refresh token and add to header
     */
    public static void refreshToken() {
        TupleNameRole tupleNameRole = ServletUtils.userNameRoleFrom.get();
        ServletUtils.addHeader.accept(
                Constant.TOKEN,
                createToken(tupleNameRole.getUsername(), tupleNameRole.getUserRole())
        );
    }
    /**
     * verify token
     *
     * @param token jwtToken
     */
    public static void verify(String token) {
        try {
            TupleNameRole tupleNameRole = tokenDecode(token);
            Algorithm algorithm = Algorithm.HMAC256(tupleNameRole.getUsername());
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim(Constant.USER_NAME, tupleNameRole.getUsername())
                    .withClaim(Constant.USER_ROLE, tupleNameRole.getUserRole())
                    .build();
            verifier.verify(token);
        } catch (JWTVerificationException e) {
            serviceLog.error("token verify fail.", e);
            throw e;
        }
    }
    /**
     * @param token token
     * @return user name and role
     */
    public static TupleNameRole tokenDecode(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return new TupleNameRole(
                    jwt.getClaim(Constant.USER_NAME).asString(),
                    jwt.getClaim(Constant.USER_ROLE).asInt()
            );
        } catch (JWTDecodeException e) {
            serviceLog.error("Token decode happen exception.", e);
            throw e;
        }
    }
}

2.9 其他的一些工具类

ServletUtils:与spring context中有关的一些方法
public final class ServletUtils {
    private ServletUtils() {
    }
    private static final int SCOPE = RequestAttributes.SCOPE_REQUEST;
    private static final Supplier<ServletRequestAttributes> servletRequestAttributes = () ->
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    private static final Supplier<HttpServletRequest> request = () -> servletRequestAttributes.get().getRequest();
    private static final Supplier<HttpServletResponse> response = () -> servletRequestAttributes.get().getResponse();
    private static final Consumer<String> saveUsernameToAttribute = (name) ->
            servletRequestAttributes.get().setAttribute(Constant.USER_NAME, name, SCOPE);
    private static final Supplier<String> usernameFromAttribute = () ->
            (String) servletRequestAttributes.get().getAttribute(Constant.USER_NAME, SCOPE);
    private static final Consumer<Integer> saveUserRoleToAttribute = (role) ->
            servletRequestAttributes.get().setAttribute(Constant.USER_ROLE, role, SCOPE);
    private static final Supplier<Integer> userRoleFromAttribute = () ->
            (Integer) servletRequestAttributes.get().getAttribute(Constant.USER_ROLE, SCOPE);
    /**
     * get token form current request
     */
    public static Supplier<String> tokenFromRequest = () -> request.get().getHeader(Constant.TOKEN);
    /**
     * save current user name and role to attribute
     */
    public static BiConsumer<String, Integer> userNameRoleTo = (name, role) -> {
        saveUsernameToAttribute.accept(name);
        saveUserRoleToAttribute.accept(role);
    };
    /**
     * get user name and role from attribute
     */
    public static Supplier<TupleNameRole> userNameRoleFrom = () ->
            new TupleNameRole(usernameFromAttribute.get(), userRoleFromAttribute.get());
    /**
     * add message to response header
     */
    public static BiConsumer<String, String> addHeader = (key, value) -> response.get().addHeader(key, value);
}

Utils:提供与shiro相同的密码加密方式、获取uuid、shiro的Filter层出错不能使用全局异常处理时的返回信息定制等。

public final class Utils {
    private Utils() {
    }
    /**
     * use sha256 encrypt
     */
    public static Function<String, String> encryptPassword = (password) -> new Sha256Hash(password).toString();
    /**
     * get uuid
     */
    public static Supplier<String> uuid = () -> UUID.randomUUID().toString().replace("-", "");
    /**
     * writer message to response
     */
    public static BiConsumer<HttpServletResponse, String> renderString = (response, body) -> {
        response.setStatus(HttpStatus.OK.value());
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=UTF-8");
        try (PrintWriter writer = response.getWriter()) {
            writer.print(body);
        } catch (IOException e) {
            serviceLog.error("response error.", e);
        }
    };
}

2.10 返回结果定义

@Data
public class ResponseResult<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    private final String code;
    @JSONField(ordinal = 1)
    private final String msg;
    @JSONField(ordinal = 2)
    private T data;
    private ResponseResult(String code, String msg) {
        this.code = code;
        this.msg = msg;
        log();
    }
    private static <T> ResponseResult<T> create(String code, String mshttp://www.chinasem.cng) {
        return new ResponseResult<>(code, msg);
    }
    /**
   android  * No data returned successfully
     *
     * @return ResponseResult<String>
     */
    public static <T> ResponseResult<T> success() {
        return success(true);
    }
    /**
     * No data returned successfully
     *
     * @param refreshToken Whether to refresh token
     * @return ResponseResult<String>
     */
    public static <T> ResponseResult<T> success(boolean refreshToken) {
        if (refreshToken) TokenUtils.refreshToken();
        return create(ErrorMessage.SUCCESS.code(), ErrorMessage.SUCCESS.msg());
    }
    public static <T> ResponseResult<T> success(T data) {
        return success(data, true);
    }
    /**
     * Data returned successfully
     *
     * @param data         data
     * @param <T>          T
     * @param refreshToken Whether to refresh token
     * @return ResponseResult<T>
     */
    public static <T> ResponseResult<T> success(T data, boolean refreshToken) {
        ResponseResult<T> responseResult = success(refreshToken);
        responseResult.setData(data);
        return responseResult;
    }
    /**
     * @param e DCException
     * @return ResponseResult<String>
     */
    public static ResponseResult<String> fail(DataAuthException e) {
        return create(e.getCode(), e.getMsg());
    }
    /**
     * @param errorMessage ErrorMessage
     * @return ResponseResult<String>
     */
    public static ResponseResult<String> fail(ErrorMessage errorMessage) {
        return create(errorMessage.code(), errorMessage.msg());
    }
    /**
     * @param errorMessage DCException
     * @return ResponseResult<String>
     */
    public static ResponseResult<String> fail(ErrorMessage errorMessage, Object[] detailMessage) {
        return create(errorMessage.code(), errorMessage.msg() + Arrays.toString(detailMessage));
    }
    // Output the information returned
    private void log() {
        requestLog.info("code:{}, msg:{}", this.getCode(), this.getMsg());
    }
}

到此这篇关于springboot项目中集成shiro+jwt详解+完整实例的文章就介绍到这了,更多相关springboot 集成shiro jwt内容请搜索编程China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于springboot项目中集成shiro+jwt完整实例代码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155629

相关文章

SpringBoot集成Shiro+JWT(Hutool)完整代码示例

《SpringBoot集成Shiro+JWT(Hutool)完整代码示例》ApacheShiro是一个强大且易用的Java安全框架,提供了认证、授权、加密和会话管理功能,在现代应用开发中,Shiro因... 目录一、背景介绍1.1 为什么使用Shiro?1.2 为什么需要双Token?二、技术栈组成三、环境

Java 与 LibreOffice 集成开发指南(环境搭建及代码示例)

《Java与LibreOffice集成开发指南(环境搭建及代码示例)》本文介绍Java与LibreOffice的集成方法,涵盖环境配置、API调用、文档转换、UNO桥接及REST接口等技术,提供... 目录1. 引言2. 环境搭建2.1 安装 LibreOffice2.2 配置 Java 开发环境2.3 配

在 Spring Boot 中连接 MySQL 数据库的详细步骤

《在SpringBoot中连接MySQL数据库的详细步骤》本文介绍了SpringBoot连接MySQL数据库的流程,添加依赖、配置连接信息、创建实体类与仓库接口,通过自动配置实现数据库操作,... 目录一、添加依赖二、配置数据库连接三、创建实体类四、创建仓库接口五、创建服务类六、创建控制器七、运行应用程序八

基于Spring Boot 的小区人脸识别与出入记录管理系统功能

《基于SpringBoot的小区人脸识别与出入记录管理系统功能》文章介绍基于SpringBoot框架与百度AI人脸识别API的小区出入管理系统,实现自动识别、记录及查询功能,涵盖技术选型、数据模型... 目录系统功能概述技术栈选择核心依赖配置数据模型设计出入记录实体类出入记录查询表单出入记录 VO 类(用于

深入解析Java NIO在高并发场景下的性能优化实践指南

《深入解析JavaNIO在高并发场景下的性能优化实践指南》随着互联网业务不断演进,对高并发、低延时网络服务的需求日益增长,本文将深入解析JavaNIO在高并发场景下的性能优化方法,希望对大家有所帮助... 目录简介一、技术背景与应用场景二、核心原理深入分析2.1 Selector多路复用2.2 Buffer

Java中数组与栈和堆之间的关系说明

《Java中数组与栈和堆之间的关系说明》文章讲解了Java数组的初始化方式、内存存储机制、引用传递特性及遍历、排序、拷贝技巧,强调引用数据类型方法调用时形参可能修改实参,但需注意引用指向单一对象的特性... 目录Java中数组与栈和堆的关系遍历数组接下来是一些编程小技巧总结Java中数组与栈和堆的关系关于

MySQL 升级到8.4版本的完整流程及操作方法

《MySQL升级到8.4版本的完整流程及操作方法》本文详细说明了MySQL升级至8.4的完整流程,涵盖升级前准备(备份、兼容性检查)、支持路径(原地、逻辑导出、复制)、关键变更(空间索引、保留关键字... 目录一、升级前准备 (3.1 Before You Begin)二、升级路径 (3.2 Upgrade

SpringBoot利用树形结构优化查询速度

《SpringBoot利用树形结构优化查询速度》这篇文章主要为大家详细介绍了SpringBoot利用树形结构优化查询速度,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一个真实的性能灾难传统方案为什么这么慢N+1查询灾难性能测试数据对比核心解决方案:一次查询 + O(n)算法解决

Python跨文件实例化、跨文件调用及导入库示例代码

《Python跨文件实例化、跨文件调用及导入库示例代码》在Python开发过程中,经常会遇到需要在一个工程中调用另一个工程的Python文件的情况,:本文主要介绍Python跨文件实例化、跨文件调... 目录1. 核心对比表格(完整汇总)1.1 自定义模块跨文件调用汇总表1.2 第三方库使用汇总表1.3 导

SpringBoot实现虚拟线程的方案

《SpringBoot实现虚拟线程的方案》Java19引入虚拟线程,本文就来介绍一下SpringBoot实现虚拟线程的方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录什么是虚拟线程虚拟线程和普通线程的区别SpringBoot使用虚拟线程配置@Async性能对比H