


Wie Springboot Authentifizierung und dynamisches Berechtigungsmanagement implementiert
Ergänzende Wissenspunkte
Shiro-Cache
Prozessanalyse
Da im Originalprojekt kein Cache konfiguriert ist, wird die Datenbank jedes Mal abgefragt, wenn überprüft werden muss, ob das aktuelle Subjekt über Zugriffsrechte verfügt. Da es sich bei Berechtigungsdaten typischerweise um Daten mit mehr Lese- und weniger Schreibzugriffen handelt, sollten wir Caching-Unterstützung hinzufügen.
Nachdem wir es zum Cache hinzugefügt haben, fragt Shiro bei der Authentifizierung zunächst den Cache nach relevanten Daten ab. Wenn es sich nicht im Cache befindet, fragt es die Datenbank ab und schreibt die gefundenen Daten in den Cache beim nächsten Mal aus dem Cache, anstatt es aus der Datenbank zu holen. Dadurch wird die Leistung unserer Anwendung verbessert.
Als nächstes implementieren wir den Cache-Verwaltungsteil von Shiro.
Shiro-Sitzungsmechanismus
Shiro bietet vollständige Sitzungsverwaltungsfunktionen auf Unternehmensebene, die nicht vom zugrunde liegenden Container abhängig sind (z. B. Webcontainer Tomcat). Er kann sowohl in JavaSE- als auch in JavaEE-Umgebungen verwendet werden Ereignisüberwachung und Sitzungsspeicherung. / Persistenz, Container-unabhängiges Clustering, Unterstützung für Invalidierung/Ablauf, transparente Unterstützung für das Web, SSO-Single-Sign-On-Unterstützung und andere Funktionen.
Wir werden Shiros Sitzungsverwaltung verwenden, um die Websitzung unserer Anwendung zu übernehmen und Sitzungsinformationen über Redis zu speichern.
Integrationsschritte
Cache hinzufügen
CacheManager
In Shiro wird die CacheManager-Klasse für die Cache-Verwaltung bereitgestellt.
Shiros Standard-EhCache-Implementierung verwenden
In Shiro wird standardmäßig das EhCache-Caching-Framework verwendet. EhCache ist ein reines Java-In-Process-Caching-Framework, das schnell und schlank ist.
Einführung der Shiro-EhCache-Abhängigkeit
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.4.0</version> </dependency>
Bei der Integration von Redis in SpringBoot müssen Sie auch auf das Problem der Versionsübereinstimmung achten, da sonst möglicherweise eine Ausnahme „Methode nicht gefunden“ gemeldet wird.
Cache-Konfiguration in ShiroConfig hinzufügen
private void enableCache(MySQLRealm realm){ //开启全局缓存配置 realm.setCachingEnabled(true); //开启认证缓存配置 realm.setAuthenticationCachingEnabled(true); //开启授权缓存配置 realm.setAuthorizationCachingEnabled(true); //为了方便操作,我们给缓存起个名字 realm.setAuthenticationCacheName("authcCache"); realm.setAuthorizationCacheName("authzCache"); //注入缓存实现 realm.setCacheManager(new EhCacheManager()); }
Rufen Sie dann diese Methode in getRealm auf.
Tipps: In dieser Implementierung ist nur lokales Caching implementiert. Mit anderen Worten: Die zwischengespeicherten Daten nutzen denselben Maschinenspeicher wie die Anwendung. Wenn der Server ausfällt oder ein unerwarteter Stromausfall auftritt, sind die zwischengespeicherten Daten nicht mehr vorhanden. Natürlich können Sie dem Cache auch über die Methode „cacheManager.setCacheManagerConfigFile()“ weitere Konfigurationen zuweisen.
Als nächstes werden wir unsere Berechtigungsdaten über Redis zwischenspeichern.
Verwenden Sie Redis zum Implementieren.
Abhängigkeiten hinzufügen -in der Konfiguration
<!--shiro-redis相关依赖--> <dependency> <groupId>org.crazycake</groupId> <artifactId>shiro-redis</artifactId> <version>3.1.0</version> <!-- 里面这个shiro-core版本较低,会引发一个异常 ClassNotFoundException: org.apache.shiro.event.EventBus 需要排除,直接使用上面的shiro shiro1.3 加入了时间总线。--> <exclusions> <exclusion> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> </exclusion> </exclusions> </dependency>
Ändern Sie die Methode doGetAuthenticationInfo
in MySQLRealm
, um das gesamte User
-Objekt als erstes von SimpleAuthenticationInfo</code zu verwenden > Parameter. shiro-redis erhält den ID-Wert aus dem ersten Parameter als Teil des Schlüssels der Daten in Redis basierend auf dem Attributwert <code>principalIdFieldName
von RedisCacheManager
.
spring: redis: host: 127.0.0.1 port: 6379 password: hewenping timeout: 3000 jedis: pool: min-idle: 5 max-active: 20 max-idle: 15
Und ändern Sie die Methode doGetAuthorizationInfo
in MySQLRealm
, um die primären Identitätsinformationen vom Benutzerobjekt abzurufen.
/**shiro配置类 * @author 赖柄沣 bingfengdev@aliyun.com * @version 1.0 * @date 2020/10/6 9:11 */ @Configuration public class ShiroConfig { private static final String CACHE_KEY = "shiro:cache:"; private static final String SESSION_KEY = "shiro:session:"; private static final int EXPIRE = 18000; @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.timeout}") private int timeout; @Value("${spring.redis.password}") private String password; @Value("${spring.redis.jedis.pool.min-idle}") private int minIdle; @Value("${spring.redis.jedis.pool.max-idle}") private int maxIdle; @Value("${spring.redis.jedis.pool.max-active}") private int maxActive; @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * 创建ShiroFilter拦截器 * @return ShiroFilterFactoryBean */ @Bean(name = "shiroFilterFactoryBean") public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); //配置不拦截路径和拦截路径,顺序不能反 HashMap<String, String> map = new HashMap<>(5); map.put("/authc/**","anon"); map.put("/login.html","anon"); map.put("/js/**","anon"); map.put("/css/**","anon"); map.put("/**","authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); //覆盖默认的登录url shiroFilterFactoryBean.setLoginUrl("/authc/unauthc"); return shiroFilterFactoryBean; } @Bean public Realm getRealm(){ //设置凭证匹配器,修改为hash凭证匹配器 HashedCredentialsMatcher myCredentialsMatcher = new HashedCredentialsMatcher(); //设置算法 myCredentialsMatcher.setHashAlgorithmName("md5"); //散列次数 myCredentialsMatcher.setHashIterations(1024); MySQLRealm realm = new MySQLRealm(); realm.setCredentialsMatcher(myCredentialsMatcher); //开启缓存 realm.setCachingEnabled(true); realm.setAuthenticationCachingEnabled(true); realm.setAuthorizationCachingEnabled(true); return realm; } /** * 创建shiro web应用下的安全管理器 * @return DefaultWebSecurityManager */ @Bean public DefaultWebSecurityManager getSecurityManager( Realm realm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm); securityManager.setCacheManager(cacheManager()); SecurityUtils.setSecurityManager(securityManager); return securityManager; } /** * 配置Redis管理器 * @Attention 使用的是shiro-redis开源插件 * @return */ @Bean public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost(host); redisManager.setPort(port); redisManager.setTimeout(timeout); redisManager.setPassword(password); JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(maxIdle+maxActive); jedisPoolConfig.setMaxIdle(maxIdle); jedisPoolConfig.setMinIdle(minIdle); redisManager.setJedisPoolConfig(jedisPoolConfig); return redisManager; } @Bean public RedisCacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); redisCacheManager.setKeyPrefix(CACHE_KEY); // shiro-redis要求放在session里面的实体类必须有个id标识 //这是组成redis中所存储数据的key的一部分 redisCacheManager.setPrincipalIdFieldName("username"); return redisCacheManager; } }
Angepasstes Salt
Da die Standardeinstellung SimpleByteSource
in Shiro die Serialisierungsschnittstelle nicht implementiert, verursacht das von ByteSource.Util.bytes() generierte Salt Fehler bei der Serialisierung, sodass eine benutzerdefinierte Salt-Klasse erforderlich ist Und implementieren Sie die Serialisierungsschnittstelle. Und verwenden Sie new CurrentSalt(user.getSalt())
, um den Salt-Wert in der benutzerdefinierten Realm-Authentifizierungsmethode zu übergeben. MySQLRealm
中的doGetAuthenticationInfo
方法,将User
对象整体作为SimpleAuthenticationInfo
的第一个参数。shiro-redis将根据RedisCacheManager
的principalIdFieldName
属性值从第一个参数中获取id值作为redis中数据的key的一部分。
/** * 认证 * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { if(token==null){ return null; } String principal = (String) token.getPrincipal(); User user = userService.findByUsername(principal); SimpleAuthenticationInfo simpleAuthenticationInfo = new MyAuthcInfo( //由于shiro-redis插件需要从这个属性中获取id作为redis的key //所有这里传的是user而不是username user, //凭证信息 user.getPassword(), //加密盐值 new CurrentSalt(user.getSalt()), getName()); return simpleAuthenticationInfo; }
并修改MySQLRealm
中的doGetAuthorizationInfo
方法,从User对象中获取主身份信息。
/** * 授权 * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { User user = (User) principals.getPrimaryPrincipal(); String username = user.getUsername(); List<Role> roleList = roleService.findByUsername(username); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); for (Role role : roleList) { authorizationInfo.addRole(role.getRoleName()); } List<Long> roleIdList = new ArrayList<>(); for (Role role : roleList) { roleIdList.add(role.getRoleId()); } List<Resource> resourceList = resourceService.findByRoleIds(roleIdList); for (Resource resource : resourceList) { authorizationInfo.addStringPermission(resource.getResourcePermissionTag()); } return authorizationInfo; }
自定义Salt
由于Shiro里面默认的SimpleByteSource
没有实现序列化接口,导致ByteSource.Util.bytes()生成的salt在序列化时出错,因此需要自定义Salt类并实现序列化接口。并在自定义的Realm的认证方法使用new CurrentSalt(user.getSalt())
/**由于shiro当中的ByteSource没有实现序列化接口,缓存时会发生错误 * 因此,我们需要通过自定义ByteSource的方式实现这个接口 * @author 赖柄沣 bingfengdev@aliyun.com * @version 1.0 * @date 2020/10/8 16:17 */ public class CurrentSalt extends SimpleByteSource implements Serializable { public CurrentSalt(String string) { super(string); } public CurrentSalt(byte[] bytes) { super(bytes); } public CurrentSalt(char[] chars) { super(chars); } public CurrentSalt(ByteSource source) { super(source); } public CurrentSalt(File file) { super(file); } public CurrentSalt(InputStream stream) { super(stream); } }
/**SessionId生成器
* <p>@author 赖柄沣 laibingf_dev@outlook.com</p>
* <p>@date 2020/8/15 15:19</p>
*/
public class ShiroSessionIdGenerator implements SessionIdGenerator {
/**
*实现SessionId生成
* @param session
* @return
*/
@Override
public Serializable generateId(Session session) {
Serializable sessionId = new JavaUuidSessionIdGenerator().generateId(session);
return String.format("login_token_%s", sessionId);
}
}
Nach dem Login kopieren
Benutzerdefinierten Sitzungsmanager hinzufügen/**SessionId生成器 * <p>@author 赖柄沣 laibingf_dev@outlook.com</p> * <p>@date 2020/8/15 15:19</p> */ public class ShiroSessionIdGenerator implements SessionIdGenerator { /** *实现SessionId生成 * @param session * @return */ @Override public Serializable generateId(Session session) { Serializable sessionId = new JavaUuidSessionIdGenerator().generateId(session); return String.format("login_token_%s", sessionId); } }
/**
* <p>@author 赖柄沣 laibingf_dev@outlook.com</p>
* <p>@date 2020/8/15 15:40</p>
*/
public class ShiroSessionManager extends DefaultWebSessionManager {
//定义常量
private static final String AUTHORIZATION = "Authorization";
private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
//重写构造器
public ShiroSessionManager() {
super();
this.setDeleteInvalidSessions(true);
}
/**
* 重写方法实现从请求头获取Token便于接口统一
* * 每次请求进来,
* Shiro会去从请求头找Authorization这个key对应的Value(Token)
* @param request
* @param response
* @return
*/
@Override
public Serializable getSessionId(ServletRequest request, ServletResponse response) {
String token = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
//如果请求头中存在token 则从请求头中获取token
if (!StringUtils.isEmpty(token)) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, token);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return token;
} else {
// 这里禁用掉Cookie获取方式
return null;
}
}
}
Nach dem Login kopieren
Benutzerdefinierten Sitzungsmanager konfigurierenKonfiguration des Sitzungsmanagers in ShiroConfig hinzufügen/** * <p>@author 赖柄沣 laibingf_dev@outlook.com</p> * <p>@date 2020/8/15 15:40</p> */ public class ShiroSessionManager extends DefaultWebSessionManager { //定义常量 private static final String AUTHORIZATION = "Authorization"; private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request"; //重写构造器 public ShiroSessionManager() { super(); this.setDeleteInvalidSessions(true); } /** * 重写方法实现从请求头获取Token便于接口统一 * * 每次请求进来, * Shiro会去从请求头找Authorization这个key对应的Value(Token) * @param request * @param response * @return */ @Override public Serializable getSessionId(ServletRequest request, ServletResponse response) { String token = WebUtils.toHttp(request).getHeader(AUTHORIZATION); //如果请求头中存在token 则从请求头中获取token if (!StringUtils.isEmpty(token)) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, token); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); return token; } else { // 这里禁用掉Cookie获取方式 return null; } } }
/** * SessionID生成器 * */ @Bean public ShiroSessionIdGenerator sessionIdGenerator(){ return new ShiroSessionIdGenerator(); } /** * 配置RedisSessionDAO */ @Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); redisSessionDAO.setSessionIdGenerator(sessionIdGenerator()); redisSessionDAO.setKeyPrefix(SESSION_KEY); redisSessionDAO.setExpire(EXPIRE); return redisSessionDAO; } /** * 配置Session管理器 * @Author Sans * */ @Bean public SessionManager sessionManager() { ShiroSessionManager shiroSessionManager = new ShiroSessionManager(); shiroSessionManager.setSessionDAO(redisSessionDAO()); //禁用cookie shiroSessionManager.setSessionIdCookieEnabled(false); //禁用会话id重写 shiroSessionManager.setSessionIdUrlRewritingEnabled(false); return shiroSessionManager; }
/** * 认证 * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { if(token==null){ return null; } String principal = (String) token.getPrincipal(); User user = userService.findByUsername(principal); SimpleAuthenticationInfo simpleAuthenticationInfo = new MyAuthcInfo( //由于shiro-redis插件需要从这个属性中获取id作为redis的key //所有这里传的是user而不是username user, //凭证信息 user.getPassword(), //加密盐值 new CurrentSalt(user.getSalt()), getName()); //清除当前主体旧的会话,相当于你在新电脑上登录系统,把你之前在旧电脑上登录的会话挤下去 ShiroUtils.deleteCache(user.getUsername(),true); return simpleAuthenticationInfo; }
@PostMapping("/login") public HashMap<Object, Object> login(@RequestBody LoginVO loginVO) throws AuthenticationException { boolean flags = authcService.login(loginVO); HashMap<Object, Object> map = new HashMap<>(3); if (flags){ Serializable id = SecurityUtils.getSubject().getSession().getId(); map.put("msg","登录成功"); map.put("token",id); return map; }else { return null; } }
/**shiro异常处理
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/10/7 18:01
*/
@ControllerAdvice(basePackages = "pers.lbf.springbootshiro")
public class AuthExceptionHandler {
//==================认证异常====================//
@ExceptionHandler(ExpiredCredentialsException.class)
@ResponseBody
public String expiredCredentialsExceptionHandlerMethod(ExpiredCredentialsException e) {
return "凭证已过期";
}
@ExceptionHandler(IncorrectCredentialsException.class)
@ResponseBody
public String incorrectCredentialsExceptionHandlerMethod(IncorrectCredentialsException e) {
return "用户名或密码错误";
}
@ExceptionHandler(UnknownAccountException.class)
@ResponseBody
public String unknownAccountExceptionHandlerMethod(IncorrectCredentialsException e) {
return "用户名或密码错误";
}
@ExceptionHandler(LockedAccountException.class)
@ResponseBody
public String lockedAccountExceptionHandlerMethod(IncorrectCredentialsException e) {
return "账户被锁定";
}
//=================授权异常=====================//
@ExceptionHandler(UnauthorizedException.class)
@ResponseBody
public String unauthorizedExceptionHandlerMethod(UnauthorizedException e){
return "未授权!请联系管理员授权";
}
}
Nach dem Login kopierenIn der tatsächlichen Entwicklung sollten die zurückgegebenen Ergebnisse vereinheitlicht und Geschäftsfehlercodes angegeben werden. Dies würde den Rahmen dieses Artikels sprengen. Bitte berücksichtigen Sie dies anhand der Eigenschaften Ihres eigenen Systems.
TestenAuthentifizierung
/**shiro异常处理 * @author 赖柄沣 bingfengdev@aliyun.com * @version 1.0 * @date 2020/10/7 18:01 */ @ControllerAdvice(basePackages = "pers.lbf.springbootshiro") public class AuthExceptionHandler { //==================认证异常====================// @ExceptionHandler(ExpiredCredentialsException.class) @ResponseBody public String expiredCredentialsExceptionHandlerMethod(ExpiredCredentialsException e) { return "凭证已过期"; } @ExceptionHandler(IncorrectCredentialsException.class) @ResponseBody public String incorrectCredentialsExceptionHandlerMethod(IncorrectCredentialsException e) { return "用户名或密码错误"; } @ExceptionHandler(UnknownAccountException.class) @ResponseBody public String unknownAccountExceptionHandlerMethod(IncorrectCredentialsException e) { return "用户名或密码错误"; } @ExceptionHandler(LockedAccountException.class) @ResponseBody public String lockedAccountExceptionHandlerMethod(IncorrectCredentialsException e) { return "账户被锁定"; } //=================授权异常=====================// @ExceptionHandler(UnauthorizedException.class) @ResponseBody public String unauthorizedExceptionHandlerMethod(UnauthorizedException e){ return "未授权!请联系管理员授权"; } }
Erfolgreiche Anmeldung
Bei falschem Benutzernamen oder Passwort
Gib aus Sicherheitsgründen den konkreten Benutzernamen nicht preis Fehler Das Passwort ist immer noch falsch.
Zugriff auf geschützte Ressourcen
Zugriff auf autorisierte Ressourcen nach Authentifizierung
#🎜🎜 ##🎜 🎜#
#🎜🎜 #
Redis anzeigen
Drei Schlüsselwerte Entsprechen dem Authentifizierungsinformationscache und den Autorisierungsinformationen Cache bzw. Sitzungsinformationscache.
Das obige ist der detaillierte Inhalt vonWie Springboot Authentifizierung und dynamisches Berechtigungsmanagement implementiert. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

AI Hentai Generator
Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen



Einführung in Jasypt Jasypt ist eine Java-Bibliothek, die es einem Entwickler ermöglicht, seinem Projekt mit minimalem Aufwand grundlegende Verschlüsselungsfunktionen hinzuzufügen und kein tiefes Verständnis der Funktionsweise der Verschlüsselung erfordert. standardbasierte Verschlüsselungstechnologie. Passwörter, Text, Zahlen, Binärdateien verschlüsseln ... Geeignet für die Integration in Spring-basierte Anwendungen, offene API, zur Verwendung mit jedem JCE-Anbieter ... Fügen Sie die folgende Abhängigkeit hinzu: com.github.ulisesbocchiojasypt-spring-boot-starter2 Die Vorteile von Jasypt schützen unsere Systemsicherheit. Selbst wenn der Code durchgesickert ist, kann die Datenquelle garantiert werden.

Nutzungsszenario 1. Die Bestellung wurde erfolgreich aufgegeben, die Zahlung erfolgte jedoch nicht innerhalb von 30 Minuten. Die Zahlung ist abgelaufen und die Bestellung wurde automatisch storniert. 2. Die Bestellung wurde unterzeichnet und es wurde 7 Tage lang keine Bewertung durchgeführt. Wenn die Bestellung abläuft und nicht ausgewertet wird, wird die Bestellung standardmäßig positiv bewertet. Wenn der Händler die Bestellung innerhalb von 5 Minuten nicht erhält, wird die Bestellung abgebrochen Es wird eine SMS-Erinnerung gesendet ... Für Szenarien mit langen Verzögerungen und geringer Echtzeitleistung können wir die Aufgabenplanung verwenden, um eine regelmäßige Abfrageverarbeitung durchzuführen. Zum Beispiel: xxl-job Heute werden wir auswählen

1. Redis implementiert das Prinzip der verteilten Sperren und warum verteilte Sperren erforderlich sind. Bevor über verteilte Sperren gesprochen wird, muss erläutert werden, warum verteilte Sperren erforderlich sind. Das Gegenteil von verteilten Sperren sind eigenständige Sperren. Wenn wir Multithread-Programme schreiben, vermeiden wir Datenprobleme, die durch den gleichzeitigen Betrieb einer gemeinsam genutzten Variablen verursacht werden. Normalerweise verwenden wir eine Sperre, um die Richtigkeit der gemeinsam genutzten Variablen sicherzustellen Die gemeinsam genutzten Variablen liegen im gleichen Prozess. Wenn es mehrere Prozesse gibt, die gleichzeitig eine gemeinsam genutzte Ressource betreiben müssen, wie können sie sich dann gegenseitig ausschließen? Heutige Geschäftsanwendungen sind in der Regel Microservice-Architekturen, was auch bedeutet, dass eine Anwendung mehrere Prozesse bereitstellen muss. Wenn mehrere Prozesse dieselbe Datensatzzeile in MySQL ändern müssen, ist eine Verteilung erforderlich, um fehlerhafte Daten zu vermeiden wird zu diesem Zeitpunkt eingeführt. Der Stil ist gesperrt. Punkte erreichen wollen

Springboot liest die Datei, kann aber nach dem Packen in ein JAR-Paket nicht auf die neueste Entwicklung zugreifen. Es gibt eine Situation, in der Springboot die Datei nach dem Packen in ein JAR-Paket nicht lesen kann ist ungültig und kann nur über den Stream gelesen werden. Die Datei befindet sich unter resources publicvoidtest(){Listnames=newArrayList();InputStreamReaderread=null;try{ClassPathResourceresource=newClassPathResource("name.txt");Input

Wenn Springboot + Mybatis-plus keine SQL-Anweisungen zum Hinzufügen mehrerer Tabellen verwendet, werden die Probleme, auf die ich gestoßen bin, durch die Simulation des Denkens in der Testumgebung zerlegt: Erstellen Sie ein BrandDTO-Objekt mit Parametern, um die Übergabe von Parametern an den Hintergrund zu simulieren dass es äußerst schwierig ist, Multi-Table-Operationen in Mybatis-plus durchzuführen. Wenn Sie keine Tools wie Mybatis-plus-join verwenden, können Sie nur die entsprechende Mapper.xml-Datei konfigurieren und die stinkende und lange ResultMap konfigurieren Schreiben Sie die entsprechende SQL-Anweisung. Obwohl diese Methode umständlich erscheint, ist sie äußerst flexibel und ermöglicht es uns

SpringBoot und SpringMVC sind beide häufig verwendete Frameworks in der Java-Entwicklung, es gibt jedoch einige offensichtliche Unterschiede zwischen ihnen. In diesem Artikel werden die Funktionen und Verwendungsmöglichkeiten dieser beiden Frameworks untersucht und ihre Unterschiede verglichen. Lassen Sie uns zunächst etwas über SpringBoot lernen. SpringBoot wurde vom Pivotal-Team entwickelt, um die Erstellung und Bereitstellung von Anwendungen auf Basis des Spring-Frameworks zu vereinfachen. Es bietet eine schnelle und einfache Möglichkeit, eigenständige, ausführbare Dateien zu erstellen

1. Passen Sie den RedisTemplate1.1-Standard-Serialisierungsmechanismus an. Die API-basierte Redis-Cache-Implementierung verwendet die RedisTemplate-Vorlage für Daten-Caching-Vorgänge. Öffnen Sie hier die RedisTemplate-Klasse und zeigen Sie die Quellcodeinformationen der Klasse publicclassRedisTemplateextendsRedisAccessorimplementsRedisOperations an. Schlüssel deklarieren, verschiedene Serialisierungsmethoden des Werts, der Anfangswert ist leer @NullableprivateRedisSe

In Projekten werden häufig einige Konfigurationsinformationen benötigt. Diese Informationen können in der Testumgebung und in der Produktionsumgebung unterschiedliche Konfigurationen haben und müssen möglicherweise später basierend auf den tatsächlichen Geschäftsbedingungen geändert werden. Wir können diese Konfigurationen nicht fest im Code codieren. Am besten schreiben Sie sie in die Konfigurationsdatei. Sie können diese Informationen beispielsweise in die Datei application.yml schreiben. Wie erhält oder verwendet man diese Adresse im Code? Es gibt 2 Methoden. Methode 1: Wir können den Wert, der dem Schlüssel in der Konfigurationsdatei (application.yml) entspricht, über den mit @Value versehenen Wert erhalten. Diese Methode eignet sich für Situationen, in denen es relativ wenige Mikrodienste gibt: Tatsächlich Projekte, wenn das Geschäft kompliziert ist, Logik
