This is the 23rd day of my participation in Gwen Challenge
Spring Boot integrates Shiro
I. Environment construction
- Maven rely on
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<fastjson.version>1.2.47</fastjson.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<! -- Shiro and Spring integration -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<! Shiro and Redis implement sessionDao -->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>
Copy the code
Second, login, permission filter implementation
Controller Login Method
@RequestMapping(value="/login")
public String login(String username,String password) {
// Construct the login token
UsernamePasswordToken upToken = new UsernamePasswordToken(username,password);
try {
/** * Password encryption: * MD5 encryption provided by Shiro * Md5Hash: * Parameter one: encrypted content * Parameter two: salt * Parameter three: encrypted times */
password = new Md5Hash(password, username, 3).toString();
/ / 1. Access to the subject
Subject subject = SecurityUtils.getSubject();
System.out.println("User Login");
//2. Call subject to log in
subject.login(upToken);
return "Login successful" + sid;
} catch (Exception e) {
return "Wrong username or password"; }}Copy the code
Custom realm
- process
- Here, the database is used to query the User object based on the user name and put the User object as security data into the SimpleAuthenticationInfo object
- So the same is true of the object that you extract when you authorize
- When authorizing, it is necessary to find out permission roles from the database and save them separately
- CustomRealm
package cn.itcast.shiro.realm;
import cn.itcast.shiro.domain.Permission;
import cn.itcast.shiro.domain.Role;
import cn.itcast.shiro.domain.User;
import cn.itcast.shiro.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashSet;
import java.util.Set;
public class CustomRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
public void setName(String name){
super.setName("CustomRealm");
}
/** * Authorization * Whether the user has the corresponding permissions during operation ** Authentication first -- security data * reauthorization -- obtain all operation permissions of the user based on security data *@param principals
* @return* /
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//1. Obtain the authenticated user data
User user = (User) principals.getPrimaryPrincipal(); // Get unique security data
//2. Obtain user permissions based on user data (all roles, all permissions)
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> roles = new HashSet<>();
Set<String> perms = new HashSet<>();
for (Role role : user.getRoles()){
roles.add(role.getName());
for (Permission perm : role.getPermissions()){
perms.add(perm.getCode());
}
}
info.setStringPermissions(perms);
info.setRoles(roles);
return info;
}
/** * Authentication *@param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//1. Login user name and password
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
String password = new String(upToken.getPassword());
//2. Query the database based on the user name
User user = userService.findByName(username);
//3. Check whether the user exists or the password is the same
if(user ! =null && user.getPassword().equals(password)){
//4. If the security data is consistently returned
// Constructor: secure data (self constructed object), password, realm name
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
return info;
}
//5. Inconsistent, return null (throw exception)
return null; }}Copy the code
Shiro configuration file
- Configure the securityManager using Spring Boot
- ShiroConfiguration
package cn.itcast.shiro;
import cn.itcast.shiro.realm.CustomRealm;
import cn.itcast.shiro.session.CustomSessionManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfiguration {
/ / 1. Create a realm
@Bean
public CustomRealm getRealm(a){
return new CustomRealm();
}
2. Create a security manager
@Bean
public SecurityManager getSecurityManager(CustomRealm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(realm);
return securityManager;
}
//3. Configure shiro filters
/** * In web applications, Shiro controls all permissions through a set of filters * map to filter different addresses, and value to determine which filters *@param securityManager
* @return* /
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
//1. Create filter factories
ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
//2. Set the security manager
filterFactory.setSecurityManager(securityManager);
//3. General configuration (login page, which is an authorized login page)
filterFactory.setLoginUrl("/autherror? code=1");// Failed to log in to the url
filterFactory.setUnauthorizedUrl("/autherror? code=2");/ / not authorized
//4. Set the filter set
/** * Set all filters: sequential map * key = blocked URL * value = filter type */
Map<String,String> filterMap = new LinkedHashMap<>();
// filterMap.put("/user/home", "anon"); // The current requested address can be accessed anonymously
// Have certain permissions to access
// Configure permission filtering as a filter
// filterMap.put("/user/home", "perms[user-home]"); // If you do not have permission, jump to the unauthorized address set above
// You must have a certain role to log in
// filtermap. put("/user/home", "roles[sysadmin]");
filterMap.put("/user/**"."authc"); // The current requested address must be authenticated to be accessible
filterFactory.setFilterChainDefinitionMap(filterMap);
return filterFactory;
}
// Configure Shiro annotation support
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
returnadvisor; }}Copy the code
The type of filter in Shiro
Methods of authorization
- It’s basically setting up a map
- The map contains interface information and permission information
- Finally, put the map into an object
/** * Set all filters: sequential map * key = blocked URL * value = filter type */
Map<String,String> filterMap = new LinkedHashMap<>();
// filterMap.put("/user/home", "anon"); // The current requested address can be accessed anonymously
// Have certain permissions to access
// Configure permission filtering as a filter
// filterMap.put("/user/home", "perms[user-home]"); // If you do not have permission, jump to the unauthorized address set above
// You must have a certain role to log in
// filtermap. put("/user/home", "roles[sysadmin]");
filterMap.put("/user/**"."authc"); // The current requested address must be authenticated to be accessible
filterFactory.setFilterChainDefinitionMap(filterMap);
Copy the code
Annotation-based authorization
- RequiresPermissions
- Configured to a method indicating the specified permissions that the method must have to execute
/ / query
@RequiresPermissions(value = "user-find")
public String find(a) {
return "User query succeeded";
}
Copy the code
- RequiresRoles
- Configured to a method, indicating that executing the method must have the specified role
/ / query
@requiresroles (value = "system administrator ")
public String find(a) {
return "User query succeeded";
}
Copy the code
Session management
What is session management
In Shiro, all user session information is controlled by Shiro. Shiro provides sessions that can be used in JavaSE/JavaEE environment, independent of any underlying container, and can be used independently as a complete session module. Unified session management through Shiro’s SessionManager
If not, what is the state of the session?
- If you don’t set session as you did before, then after the user login, all data will be stored in the HttpSession, and a sessionId will be returned, stored in the cookie
Build environment
- Shiro and REids integration dependencies
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.0.0</version>
</dependency>
Copy the code
- Add redis configuration to the SpringBoot configuration file
redis:
host: 192.16885.171.
port: 6379
Copy the code
Customize our sessionManager
package cn.itcast.shiro.session;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
/** * custom sessionManager */
public class CustomSessionManager extends DefaultWebSessionManager {
/** * specifies the method of obtaining the sessionId. * the sessionId * request header information contains the Authorization: sessionId */
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
// Get the data in the request header
String id = WebUtils.toHttp(request).getHeader("Authorization");
if(StringUtils.isEmpty(id)){
// if the first access does not have a sessionId, generate a sessionId
// the parent method can generate a sessionId, so call the parent method directly
return super.getSessionId(request, response);
}else{
/ / returns the sessionId
// Where does the sessionId come from: the request header
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");
// What is the sessionId
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
// Does this sessionId need validation
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
returnid; }}}Copy the code
Configure Redis Session management
/ * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the session management -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /
/** * Why put session in redis? * 1. If the session is not stored in redis, the session is stored in the memory of the current service and cannot be accessed by other services. Obviously, for distributed microservices, each service can synchronize data * 3. If you don't put it in reids, it's like a normal session */
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
//1. Controller of redis, operating redis
public RedisManager redisManager(a){
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
redisManager.setPassword("123456");
return redisManager;
}
//2. sessionDao
public RedisSessionDAO reidsSessionDao(a){
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
// If you look at shiro's architecture diagram, you can see that reidsDao is managed using RedisManager
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
//3. Session management
public DefaultWebSessionManager sessionManager(a){
CustomSessionManager sessionManager = new CustomSessionManager();
// This step is similar to the above logic
sessionManager.setSessionDAO(reidsSessionDao());
return sessionManager;
}
//4. Cache manager
public RedisCacheManager cacheManager(a){
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
/ * -- -- -- -- -- -- -- -- -- -- -- -- -- end of the session management configuration -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /
Copy the code