1. Rights management
1.1 What Is Rights Management?
Permission management Allows users to access only authorized resources based on security rules or policies
Permission management includes user identity authentication and authorization, which are referred to as authentication and authorization
1.2 What is Identity Authentication?
Identity authentication is the process of checking whether a user is a valid user. The most common method is to check whether the user’s input and user name and password are consistent with those stored in the system
1.3 What is Authorization?
Authorization, namely, access control, controls who can access which resources. After identity authentication, subjects need to assign permissions to access system resources. Some resources cannot be accessed without permissions
2. What is Shiro?
Shiro is a powerful and easy-to-use Java security framework capable of authentication, authorization, encryption, and session management
2.1 Shiro’s core architecture
-
Subject
Subject, the external application interacts with the Subject, who records the current operating user and understands the concept of user as the subject of the current operation, either a user requesting through a browser or a running program. Subject is an interface in Shiro, and many authentication methods are defined in the interface. External programs are authenticated by Subject, while Subject is authenticated and authorized by SecurityManager
-
SecurityManager
The security manager, which manages security for all subjects, is at the heart of Shiro. You can use the SecurityManager to authenticate and authorize a subject. Essentially, the SecurityManager authenticates a subject using an Authenticator and Authorizer. Session management is performed through the SessionManager. SecurityManager is an interface that inherits the Authenticator, Authorizer, and SessionManager interfaces
-
Authenticator
Authentication, the user identity authentication, the Authenticator is an interface, shiro provides ModularRealmAuthenticator implementation class, through ModularRealmAuthenticator basically can satisfy most requirements, You can also customize the authenticator
-
Authorizer
Authenticator: The user is authenticated by the authenticator. When accessing the function, the user needs to check whether the user has the operation permission for the function
-
Realm
Realm, which is the equivalent of a datasource datasource, requires the securityManager to obtain user permission data from the Realm for security authentication. For example, if user identity data is in the database, then the Realm needs to obtain user identity information from the database
-
SessionManager
Session management, Shiro framework defines a set of session management, it does not rely on the Web container session, so Shiro can be used in non-Web applications, distributed applications can be centralized session management, this feature enables it to achieve single sign-on
-
SessionDAO
Session DAO is a set of interfaces for session operations. For example, to store a session to a database, you can use JDBC to store a session to a database
-
CacheManager
CacheManager stores user permission data in the cache to improve performance
-
Cryptography
Password management, Shiro provides a set of encryption and decryption components for easy development, such as providing common hashing, encryption and decryption functions
3. Authentication in Shiro
3.1 certified
Identity authentication is the process of determining whether a user is a legitimate user. The most common authentication method is that the system checks whether the user name and password entered by the user are consistent with the user name and password stored in the system to determine whether the user identity is correct
3.2 Key objects in ShirO Authentication
-
Subject: the Subject
Users who access the system, subjects can be users, programs, etc., and those who authenticate are called subjects
-
Principal: Identity information
It is the identification of the subject for identity authentication, which must be unique, such as user name, mobile phone number, email address, etc. A subject can have multiple identities, but it must have a Primary Principal identity.
-
B: Credential information
Is security information known only to the subject, such as passwords, certificates, etc
3.3 Authentication Process
3.4 Authentication Development
3.4.1 Introducing dependencies
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.3</version>
</dependency>
Copy the code
3.4.2 Creating a Configuration File
The configuration file is used to write permission data in the system for learning
[users]
xiaochen=123
zhangsan=456
Copy the code
3.4.3 Developing authentication codes
public class TestAuthenticator {
public static void main(String[] args) {
// 1. Create a security manager object
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 2. Set the realm for the security manager
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
// 3. Set the security manager for the SecurityUtils global security tool class
SecurityUtils.setSecurityManager(securityManager);
// 4. Obtain the authentication body
Subject subject = SecurityUtils.getSubject();
// 5. Create token
UsernamePasswordToken token = new UsernamePasswordToken("xiaochen"."123");
/ / 6. Certification
try {
System.out.println("Certification Status:" + subject.isAuthenticated());
subject.login(token);
System.out.println("Certification Status:" + subject.isAuthenticated());
} catch(Exception e) { e.printStackTrace(); }}}Copy the code
3.5 Customize the Realm
A custom Realm implementation is an implementation that transforms the authentication/authorization data source into a database
public class TestCustomerRealmAuthenticator {
public static void main(String[] args) {
// 1. Create a security manager object
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 2. Set custom realms
securityManager.setRealm(new CustomerRealm());
// 3. Set the security manager for the SecurityUtils global security tool class
SecurityUtils.setSecurityManager(securityManager);
// 4. Obtain the authentication body
Subject subject = SecurityUtils.getSubject();
// 5. Create token
UsernamePasswordToken token = new UsernamePasswordToken("xiaochen"."123");
/ / 6. Certification
try {
System.out.println("Certification Status:" + subject.isAuthenticated());
subject.login(token);
System.out.println("Certification Status:" + subject.isAuthenticated());
} catch(Exception e) { e.printStackTrace(); }}}Copy the code
Custom Realm code implementation
public class CustomerRealm extends AuthorizingRealm {
/ / authorization
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/ / certification
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 1. Obtain the user name from the token
String principal = (String) authenticationToken.getPrincipal();
// 2. Query database data
if ("xiaochen".equals(principal)) {
// Parameter 1: the correct user name
// Parameter 2: the correct password
// Argument 3: Provides the name of the current realm
SimpleAuthenticationInfo simpleAuthenticationInfo
= new SimpleAuthenticationInfo("xianchen"."123".this.getName());
return simpleAuthenticationInfo;
}
return null; }}Copy the code
3.6 Plaintext Encryption
In practice, it is impossible to display the user password in plain text, so it needs to be encrypted
The usual encryption method is md5 + salt + hash. The verification process is as follows: save the salt and hashed value, and complete the password verification in Shiro
The following is an example of code to do this using the API provided by Shiro
public class TestShiroMD5 {
public static void main(String[] args) {
// 1. Use MD5 encryption
// Parameter 1 is the plaintext password
Md5Hash md5Hash1 = new Md5Hash("123");
// Prints the encrypted ciphertext
/ / the result: 202 cb962ac59075b964b07152d234b70
System.out.println(md5Hash1.toHex());
// 2. Use MD5 +salt encryption
Md5Hash md5Hash2 = new Md5Hash("123"."X0*7ps");
// 8a83592a02263bfe6752b2b5b03a4799
System.out.println(md5Hash2.toHex());
// 3. Use MD5 +salt+hash for encryption
Md5Hash md5Hash3 = new Md5Hash("123"."X0*7ps".1024);
// e4f9bf3e0c58f045e62c23c533fcf633System.out.println(md5Hash3.toHex()); }}Copy the code
Custom CustomerMd5Realm
public class CustomerMd5Realm extends AuthorizingRealm {
/ / authorization
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/ / certification
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 1. Obtain the user name from the token
String principal = (String) authenticationToken.getPrincipal();
// 2. Query database data
if ("xiaochen".equals(principal)) {
// Parameter 1: the correct user name
// Parameter 2: the correct password
// Argument 3: Provides the name of the current realm
// md5
// return new SimpleAuthenticationInfo(principal, "202cb962ac59075b964b07152d234b70", this.getName());
// md5+salt
/*return new SimpleAuthenticationInfo(principal, "8a83592a02263bfe6752b2b5b03a4799", ByteSource.Util.bytes("X0*7ps"), this.getName()); * /
}
return null; }}Copy the code
Check the process
public class TestCustomerMd5RealmAuthenticator {
public static void main(String[] args) {
// 1. Create a security manager object
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 2. Set custom realms
CustomerMd5Realm realm = new CustomerMd5Realm();
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// Use MD5 encryption
hashedCredentialsMatcher.setHashAlgorithmName("md5");
// Hash times
hashedCredentialsMatcher.setHashIterations(1024);
realm.setCredentialsMatcher(hashedCredentialsMatcher);
securityManager.setRealm(realm);
// 3. Set the security manager for the SecurityUtils global security tool class
SecurityUtils.setSecurityManager(securityManager);
// 4. Obtain the authentication body
Subject subject = SecurityUtils.getSubject();
// 5. Create token
UsernamePasswordToken token = new UsernamePasswordToken("xiaochen"."123");
/ / 6. Certification
try {
System.out.println("Certification Status:" + subject.isAuthenticated());
subject.login(token);
System.out.println("Certification Status:" + subject.isAuthenticated());
} catch(Exception e) { e.printStackTrace(); }}}Copy the code
4. Authorization in Shiro
4.1 license
Authorization, or access control, controls who can access what resources. After identity authentication, the principal needs to assign permissions to access system resources. Some resources without permissions cannot be accessed
4.2 Key Objects
Authorization can be simply understood as who does How to what(which) :
- Who, or Subject, needs to access resources in the system
- What refers to resources, such as system menus, pages, buttons, class methods, system commodity information, etc. Resources include resource types and resource instances. For example, if the commodity information is resource type, the commodity whose type is T01 is resource instance, and the commodity whose id is 001 is resource instance
- How, Permission, defines the operation Permission of the subject on resources. The Permission is meaningless without resources, such as the user’s query Permission, the user’s add Permission, the call Permission of a certain class method, and the modification Permission of the user numbered 001, etc. By Permission, the subject can know the operation Permission of the resource
4.3 Authorization Mode
Role-based access control: Role-centered access control
if(subject.hasRole("admin")) {// What resource is operated on
}
Copy the code
Resource-based access control, resource-centered access control
if(subject.isPermission("user:update:01")) {// Resource instance
// Modify user 01
}
if(subject.isPermission("user:update:*")) {// Resource type
// Modify user 01
}
Copy the code
4.4 Permission String
The rules for permission strings are as follows: Resource identifier: Operation: Resource instance identifier, which means what operation on which instance of which resource, : is the resource/operation/instance separator, permission strings can also use * wildcard
Example:
- User creation permission:
user:create
Or,user:create:*
- User changed the permission of instance 001:
user:update:001
- All permissions for user instance 001:
User: * : 001
4.5 Authorized programming implementation
On the basis of the previous MD5 encryption, realize the authorization operation
Custom CustomerMd5Realm
public class CustomerMd5Realm extends AuthorizingRealm {
/ / authorization
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// Get the identity information
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
// Obtain the role and permission information of the current user from the database based on the identity information (user name)
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
// Assign the role information queried in the database to the permission object
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addRole("user");
// Assign the permission information queried in the database to the permission object
simpleAuthorizationInfo.addStringPermission("user:*:01");
simpleAuthorizationInfo.addStringPermission("product:create:02");
returnsimpleAuthorizationInfo; }... }Copy the code
Authorization logic
public class TestCustomerMd5RealmAuthenticator {
public static void main(String[] args) {...// 7. Authenticate the user for authorization
if (subject.isAuthenticated()) {
// 7.1 Role-based Permission Control
boolean hasRole = subject.hasRole("admin");
System.out.println("Role verification:" + hasRole);
7.2 Permission Control Based on Multiple Roles
boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("admin"."user"));
System.out.println("Multi-role verification:" + hasAllRoles);
// 7.3 Whether to have one of the roles
boolean[] booleans = subject.hasRoles(Arrays.asList("admin"."user"."super"));
for (boolean aBoolean : booleans) {
System.out.println(aBoolean);
}
// 7.4 Access control based on permission string
boolean permitted = subject.isPermitted("user:*:01");
System.out.println("Resource permission Verification:" + permitted);
7.5 What resource rights does the Distribution have
boolean[] permitted1 = subject.isPermitted("user:*:01"."order:*:10");
for (boolean b : permitted1) {
System.out.println(b);
}
// 7.6 What Resource Permissions Are Available at the same time
boolean permittedAll = subject.isPermittedAll("user:*:01"."product:*");
System.out.println("Multi-resource permission verification:"+ permittedAll); }}}Copy the code
5. Shiro integrates SpringBoot
Shiro’s idea of integrating SpringBoot is shown below:
Shiro was introduced to integrate SpringBoot dependencies
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.5.3</version>
</dependency>
Copy the code
5.1 certified
Configure shiro
@Configuration
public class ShiroConfig {
// 1. Create shiroFilter to block all requests
@Bean
public ShiroFilterFactoryBean getShiroFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// Set the security manager for filter
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// Configure system limited resources
HashMap<String, String> map = new HashMap<>();
map.put("/user/login"."anon"); // Set anon to a public resource
map.put("/user/register"."anon");
map.put("/register.jsp"."anon");
map.put("/ * *"."authc"); // Authc indicates that requesting this resource requires authentication and authorization
// Path of the default authentication interface
shiroFilterFactoryBean.setLoginUrl("/login.jsp");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
2. Create a security manager
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("realm") Realm realm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// Set the Realm for the security manager
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
// create a custom realm
@Bean(name = "realm")
public Realm getRealm(a) {
CustomerRealm customerRealm = new CustomerRealm();
// Modify the credential verifier
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
// Set the encryption algorithm to MD5
credentialsMatcher.setHashAlgorithmName("MD5");
// Set the hash count
credentialsMatcher.setHashIterations(1024);
customerRealm.setCredentialsMatcher(credentialsMatcher);
returncustomerRealm; }}Copy the code
public class CustomerRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String principal = (String) authenticationToken.getPrincipal();
UserSev userSev = (UserSev) ApplicationContextUtils.getBean("userSev");
User user = userSev.findByUsername(principal);
if(user ! =null) {
return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),
ByteSource.Util.bytes(user.getSalt()), this.getName());
}
return null; }}Copy the code
Shiro provides multiple default filters that you can use to control permissions for specific urls
Configuration for | Corresponding filter | function |
---|---|---|
anon | AnonymousFilter | Specifies that the URL can be accessed anonymously |
authc | FormAuthenticationFilter | Specifies that the URL requires a form login, which is obtained from the request by defaultusername ,password .rememberMe If the login fails, the system switches to the loginUrl path. We can also use this filter as the default login logic, but we usually write our own login logic in the controller, so we can customize the message returned if we write it. |
authcBasic | BasicHttpAuthenticationFilter | Basic login is required to specify the URL |
logout | LogoutFilter | Logout filter, configure the specified URL can realize the exit function, very convenient |
noSessionCreation | NoSessionCreationFilter | Disabling session creation |
perms | PermissionsAuthorizationFilter | You need to specify permission to access |
port | PortFilter | You need to specify a port for access |
rest | HttpMethodPermissionFilter | Convert the HTTP request method into the corresponding verb to construct a permission string, this feeling is not meaningful, interested in looking at the source code comments |
roles | RolesAuthorizationFilter | You need to specify a role to access |
ssl | SslFilter | An HTTPS request is required for access |
user | UserFilter | You need to be logged in or Remember Me to access it |
Simulate the authentication, registration, and exit process
@Controller
@RequestMapping("user")
public class UserCon {
@Autowired
private UserSev userSev;
@RequestMapping("logout")
public String logout(String username, String password) {
// Get the body object
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:/login.jsp";
}
@RequestMapping("login")
public String login(String username, String password) {
// Get the body object
Subject subject = SecurityUtils.getSubject();
try {
subject.login(new UsernamePasswordToken(username, password));
return "redirect:/index.jsp";
} catch (UnknownAccountException e) {
System.out.println("User name error");
} catch (IncorrectCredentialsException e) {
System.out.println("Password error");
}
return "redirect:/index.jsp";
}
@RequestMapping("register")
public String register(User user) {
try {
userSev.register(user);
} catch (Exception e) {
return "redirect:/register.jsp";
}
return "redirect:/login.jsp"; }}Copy the code
@Service
public class UserSev {
@Autowired
private UserDao userDao;
public void register(User user) {
// Process business calls to dao
// The plaintext password is used for md5 + salt + hash
String salt = SaltUtils.getSalt();
user.setSalt(salt);
Md5Hash md5Hash = new Md5Hash(user.getPassword(), salt, 1024);
user.setPassword(md5Hash.toHex());
userDao.save(user);
}
public User findByUsername(String username) {
returnuserDao.findByUserName(username); }}Copy the code
5.2 license
The first way is through page tag authorization
The < %@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<shiro:hasAnyRoles name="user,admin">
<li><a href=""> User management </a> <ul> <shiro:hasPermission name="user:add:*">
<li><a href=""</a></li> </shiro:hasPermission> <shiro:hasPermission name="user:delete:*">
<li><a href=""</a></li> </shiro:hasPermission> <shiro:hasPermission name="user:update:*">
<li><a href=""</a></li> </shiro:hasPermission> <shiro:hasPermission name="user:find:*">
<li><a href=""> Query </a></li> </shiro:hasPermission> </ul> </li> </shiro:hasAnyRoles> <shiro:hasRole name="admin">
<li><a href=""</a></li> <li><a href=""> Order management </a></li> <li><a href=""></ a></li> </shiro:hasRole>Copy the code
The second method is code authorization
@RequestMapping("save")
public String save(a){
System.out.println("Method of entry");
// Get the body object
Subject subject = SecurityUtils.getSubject();
// Code mode
if (subject.hasRole("admin")) {
System.out.println("Save order!");
}else{
System.out.println("No access!");
}
// Based on the permission string
//....
return "redirect:/index.jsp";
}
Copy the code
The second method is annotation authorization
@RequiresRoles(value={"admin","user"})// Check whether the role has admin user
@RequiresPermissions("user:update:01") // To determine the permission string
@RequestMapping("save")
public String save(a){
System.out.println("Method of entry");
return "redirect:/index.jsp";
}
Copy the code
This is just a demonstration. In real development, we need to persist authorization data. Three tables are required: user table, role table, and permission table. The relationship between user table and role table and between role table and permission table is many-to-many. You need to create a relationship table
Modify custom Realms
public class CustomerRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// Get the identity information
String primaryPrincipal = (String) principals.getPrimaryPrincipal();
System.out.println("Invoke authorization authentication:"+primaryPrincipal);
// Obtain role and permission information based on master identity information
UserService userService = (UserService) ApplicationContextUtils.getBean("userService");
User user = userService.findRolesByUserName(primaryPrincipal);
// Authorization role information
if(! CollectionUtils.isEmpty(user.getRoles())){ SimpleAuthorizationInfo simpleAuthorizationInfo =new SimpleAuthorizationInfo();
user.getRoles().forEach(role->{
simpleAuthorizationInfo.addRole(role.getName());
// Permission information
List<Perms> perms = userService.findPermsByRoleId(role.getId());
if(!CollectionUtils.isEmpty(perms)){
perms.forEach(perm->{
simpleAuthorizationInfo.addStringPermission(perm.getName());
});
}
});
return simpleAuthorizationInfo;
}
return null; }}Copy the code
5.3 shiro cache
The use of cache can reduce the DB access pressure, so as to improve the efficiency of the system
5.3.1 integration Ehcache
Ehcache dependencies are introduced
<! -- Introducing shiro and Ehcache
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.5.3</version>
</dependency>
Copy the code
Open the cache
@Bean
public Realm getRealm(a){
CustomerRealm customerRealm = new CustomerRealm();
// Modify the credential verifier
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
// Set the encryption algorithm to MD5
credentialsMatcher.setHashAlgorithmName("MD5");
// Set the hash count
credentialsMatcher.setHashIterations(1024);
customerRealm.setCredentialsMatcher(credentialsMatcher);
// Enable cache management
customerRealm.setCacheManager(new EhCacheManager());
customerRealm.setCachingEnabled(true);// Enable global caching
customerRealm.setAuthenticationCachingEnabled(true);// Authentication authentication cache
customerRealm.setAuthenticationCacheName("authenticationCache");
customerRealm.setAuthorizationCachingEnabled(true);// Enable authorization caching
customerRealm.setAuthorizationCacheName("authorizationCache");
return customerRealm;
}
Copy the code
5.3.2 integrate Redis
Introduce redis dependencies
<! - integrate redis springboot -- -- >
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Copy the code
Configure the Redis connection and start the Redis service
spring.redis.port=6379
spring.redis.host=localhost
spring.redis.database=0
Copy the code
Ehcache provides EhCacheManager, and EhCacheManager implements the CacheManager interface, so we can customize a RedisCacheManager
public class RedisCacheManager implements CacheManager {
@Override
public <K, V> Cache<K, V> getCache(String cacheName) throws CacheException {
System.out.println("Cache name:"+cacheName);
return newRedisCache<K,V>(cacheName); }}Copy the code
Then customize the Cache interface implementation
public class RedisCache<K.V> implements Cache<K.V> {
private String cacheName;
public RedisCache(a) {}public RedisCache(String cacheName) {
this.cacheName = cacheName;
}
@Override
public V get(K k) throws CacheException {
System.out.println("Get cache :"+ k);
return (V) getRedisTemplate().opsForHash().get(this.cacheName,k.toString());
}
@Override
public V put(K k, V v) throws CacheException {
System.out.println("Set cache key:"+k+" value:"+v);
getRedisTemplate().opsForHash().put(this.cacheName,k.toString(),v);
return null;
}
@Override
public V remove(K k) throws CacheException {
return (V) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString());
}
@Override
public v remove(k k) throws CacheException {
return (v) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString());
}
@Override
public void clear(a) throws CacheException {
getRedisTemplate().delete(this.cacheName);
}
@Override
public int size(a) {
return getRedisTemplate().opsForHash().size(this.cacheName).intValue();
}
@Override
public Set<k> keys(a) {
return getRedisTemplate().opsForHash().keys(this.cacheName);
}
@Override
public Collection<v> values(a) {
return getRedisTemplate().opsForHash().values(this.cacheName);
}
private RedisTemplate getRedisTemplate(a){
RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
// Encapsulate the redisTemplate
private RedisTemplate getRedisTemplate(a){
RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
returnredisTemplate; }}Copy the code
We also need a serialization implementation for SALT, and since the SimpleByteSource implementation provided with Shiro does not implement serialization, we need a custom salt serialization implementation
// A custom salt implementation implements the serialization interface
public class MyByteSource extends SimpleByteSource implements Serializable {
public MyByteSource(String string) {
super(string); }}Copy the code
Use custom salt in realm
public class MyByteSource implements ByteSource.Serializable {
private byte[] bytes;
private String cachedHex;
private String cachedBase64;
// Add parameterless constructors for serialization and deserialization
public MyByteSource(a){}public MyByteSource(byte[] bytes) {
this.bytes = bytes;
}
public MyByteSource(char[] chars) {
this.bytes = CodecSupport.toBytes(chars);
}
public MyByteSource(String string) {
this.bytes = CodecSupport.toBytes(string);
}
public MyByteSource(ByteSource source) {
this.bytes = source.getBytes();
}
public MyByteSource(File file) {
this.bytes = (new MyByteSource.BytesHelper()).getBytes(file);
}
public MyByteSource(InputStream stream) {
this.bytes = (new MyByteSource.BytesHelper()).getBytes(stream);
}
public static boolean isCompatible(Object o) {
return o instanceof byte[] || o instanceof char[] || o instanceof String || o instanceof ByteSource || o instanceof File || o instanceof InputStream;
}
public byte[] getBytes() {
return this.bytes;
}
public boolean isEmpty(a) {
return this.bytes == null || this.bytes.length == 0;
}
public String toHex(a) {
if (this.cachedHex == null) {
this.cachedHex = Hex.encodeToString(this.getBytes());
}
return this.cachedHex;
}
public String toBase64(a) {
if (this.cachedBase64 == null) {
this.cachedBase64 = Base64.encodeToString(this.getBytes());
}
return this.cachedBase64;
}
public String toString(a) {
return this.toBase64();
}
public int hashCode(a) {
return this.bytes ! =null && this.bytes.length ! =0 ? Arrays.hashCode(this.bytes) : 0;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof ByteSource) {
ByteSource bs = (ByteSource)o;
return Arrays.equals(this.getBytes(), bs.getBytes());
} else {
return false; }}private static final class BytesHelper extends CodecSupport {
private BytesHelper(a) {}public byte[] getBytes(File file) {
return this.toBytes(file);
}
public byte[] getBytes(InputStream stream) {
return this.toBytes(stream); }}}Copy the code