• notice
  • Congratulations on the launch of the Sought Tech site

3 ways to integrate Shiro with SpringBoot

Shiro is mainly used for permission management. A brief introduction is as follows:

1. Concept

Shiro is a security framework that can manage roles and permissions.

The main functions of Shiro are as follows:
Authentication: User identification, often referred to as user "login"
Authorization: Access control. For example, whether a user has permission to use an operation.
Session Management: User-specific session management, even in non-web or EJB applications.
Cryptography: Ease of use is guaranteed while encrypting the data source with a cryptographic algorithm.
The main category

1.Subject: The current user, Subject can be a person or a third-party service
2.SecurityManager: Manages all Subjects and can cooperate with internal security components.

3.principals: Identity, that is, the identity attribute of the subject, can be anything, such as username, email, etc., it is unique. A principal can have multiple principals, but there is only one Primary principals, usually username/password/mobile phone number.
4.credentials: certificates/credentials, that is, security values that only the subject knows, such as passwords/digital certificates, etc.
The most common combination of principals and credentials is username/password.

5.Realms: It is used to verify the permission information and needs to be implemented by yourself.
6.Realm is essentially a specific security DAO: it encapsulates the details of connecting with the data source to get the relevant data needed by Shiro.
When configuring Shiro, you must specify at least one Realm to implement authentication and/or authorization.
We need to implement Realms Authentication and Authorization. Among them, Authentication is used to verify the user's identity, and Authorization is used to authorize access control, which is used to authorize the user's operation to prove whether the user is allowed to perform the current operation, such as accessing a link, a resource file, etc.

7. SimpleHash can encrypt the password multiple times through a specific algorithm (such as md5) and the salt value salt.

3. Shiro configuration

1. Spring integrated Shiro is generally configured through xml, and Spring Boot integrated Shiro is generally configured through java code with @Configuration and @Bean configuration.

2. The core of Shiro is realized by the filter Filter. The Filter in Shiro uses URL rules to filter and verify permissions, so we need to define a series of URL rules and access permissions.

3. SpringBoot integrates Shiro, we need to write two main classes, the ShiroConfiguration class, and the Realm class that inherits AuthorizingRealm

The ShiroConfiguration class is used to configure Shiro and inject various beans.

Including filter (shiroFilter), security transaction manager (SecurityManager), password credentials (CredentialsMatcher), aop annotation support (authorizationAttributeSourceAdvisor), etc.

Realm class, including login authentication (doGetAuthenticationInfo), authorization authentication (doGetAuthorizationInfo)

 

Four: the actual combat part

Two users are shown here to log in to the system, with different permissions, so that users can only operate the menus under their own permissions, and cannot operate other menus. The menu demo here uses 2 hard-coded menus. Based on SpringBoot, a total of 3 implementation methods are used to demonstrate the advantages and disadvantages, and choose according to the actual situation of the individual.

 

4.1, the public part, the introduction of dependencies

<!–shiro –>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId >org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!– shiro ehcache –>
<dependency>
<groupId>org.apache .shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<exclusions>
<exclusion>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
</exclusion>
</exclusions>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>

 

 

4.2, the database part, everything is simplified

81123174649.png

4.3. There are different parts at the beginning, the first method is marked with yellow, the second is marked with green, and the third is marked with blue.

Configure a Realm to implement login authentication and authorization,

import com.example.demo.entity.User;
import com.example.demo.mapper.RoleMapper;
import com.example.demo.mapper.UserMapper;
import org.apache.shiro.SecurityUtils;
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.List;
import java.util.Set;


/**
Customized Realm implements authorization and authentication
*/
public class UserRealm extends AuthorizingRealm {
//Note here that the following automatic loading may be empty, private UserMapper is configured in the shrio configuration class
userMapper;
private RoleMapper roleMapper;
@Autowired
private void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Autowired
private void setRoleMapper(RoleMapper roleMapper) {
this.roleMapper = roleMapper;
}
/**
authorization
*
@param
 principals
@return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
User user = (User) SecurityUtils.getSubject () .getPrincipal ();
Set<String> roles = new HashSet<>();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
List<String> role = roleMapper.findRoleByUser(user.getUserName ());
role.stream().forEach(s -> {
roles.add(s);
});
info.setRoles(roles);

/**
*  
If you need to use shiro:hasPermission=”guest” in the page * You need
to configure shiroDialect in shrio, and add the following sentence
*/
//info.addStringPermissions(role);
return info;
}

/**
Authentication
*
@param
 token
@return
@throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken usertoken = (UsernamePasswordToken) token;
User user = userMapper.selectByUserName(usertoken.getUsername( ));
if (user == null) {
throw new AccountException("Username does not exist");
}
if (!user.getPassWord().equals(new String((char[]) usertoken.getCredentials())) ) {
throw new AccountException("Incorrect password");
}
return new SimpleAuthenticationInfo(user, usertoken.getPrincipal(), getName());
}
}

 

4.4 Configure the shrio configuration class, where cache related information can also be configured.

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver; import java.util.LinkedHashMap;
import java.util.Map;


@Configuration
public class ShrioConfig {
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// SecurityManager must be set
shiroFilterFactoryBean.setSecurityManager(securityManager);
// setLoginUrl If no value is set, the web project will be automatically searched by default The "/login.jsp" page or "/login" in the root directory maps
shiroFilterFactoryBean.setLoginUrl("/");
// Set the url to jump when no permission;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
// Set Interceptor
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//Visitor, development permission
filterChainDefinitionMap.put("/errorPage", "anon");
//User, requires role permission "user" //Management member, the role permission "admin" is required
        //Note: This is the first configuration, which is the permission and path information directly written here      filterChainDefinitionMap.put("/user/addUser", "roles[admin]");         filterChainDefinitionMap.put("/user/ selectUser", "roles[guest]");  //Note that the following is the second way, and only one can be configured above // filterChainDefinitionMap.put("/user/addUser", "authc"); // filterChainDefinitionMap.put ("/user/selectUser", "authc");
  



//Open login/exit interface
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/logout", "anon");
//The rest of the interface will be intercepted
//The main line of code must be placed The last of all permission settings, otherwise all urls will be intercepted
filterChainDefinitionMap.put("/**", "authc");

shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
System.out .println ("Shiro interceptor factory class injected successfully");
return shiroFilterFactoryBean;
}

/**
Inject securityManager
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// set realm.
securityManager.setRealm(customRealm());
return securityManager;
}

/**
* ShiroDialect 
to use shiro-labeled bean in thymeleaf
*
@return
*/
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}

/**
Handle the jump problem when unauthorized
*
@return
*/
@Bean
public HandlerExceptionResolver solver() {
HandlerExceptionResolver handlerExceptionResolver = new MyExceptionResolver();
return handlerExceptionResolver;
}

/**
Enable shiro aop annotation support. Required
* use proxy mode; so you need to enable code support; otherwise annotations such as @RequiresRoles will not take effect
*
@param
 securityManager
@return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}

@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}

/**
Custom authentication realm;
<p>
This class must be written and annotated with @Bean, the purpose is to inject CustomRealm,
* Otherwise, it will affect the dependency injection of other classes in the CustomRealm class
*/
@Bean
public UserRealm customRealm() {
return new UserRealm();
}
}

4.5. Part of the logic in LoginController (login, logout, 403, error and other pages)

@RequestMapping("/")
public String login() {
return "login";
/**
error page
@return
*/
@RequestMapping("/errorPage")
public String error() {
return "/error";
}


/**
Unauthorized redirect page
@return
*/
@RequestMapping("/403")
public String error403() {
return "403";
}

/**
User Center
@param
 request
@return
*/

@RequestMapping("/home")
public String selectInformation(HttpServletRequest request) {
return "index";
}

/**
logout
@param
 request
@return
*/
@RequestMapping("/loginOut")
public String loginOut(HttpServletRequest request) {
SecurityUtils.getSubject () .logout ();
return "redirect:/";
}

/**
Processing login logic
@param
 request
@return
*/
@RequestMapping(value = "/login", method = RequestMethod.POST ) public String toLogin
(HttpServletRequest request) {
String userName = request.getParameter("userName" );
String passwd = request.getParameter("password");
UsernamePasswordToken token = new UsernamePasswordToken(userName, passwd);
Subject subject = SecurityUtils.getSubject (); try { subject.login (token); return "redirect:/home" ; } catch (Exception e) { request.getSession().setAttribute("msg", "Login failed, incorrect username or password");





return "redirect:/errorPage";
}
}

 

4.6 The landing page is omitted, here is the home page of the successful landing page

//Note: The first and third types are written in this way, and the second one is annotated below


<h1><a th:href=”@{/user/addUser}” >add user-admin permissions</a></h1>
<h1><a th:href=”@{/user/selectUser}” >User list – guest permissions</a></h1>

<!–<h1><a th:href=”@{/user/addUser}” shiro:hasPermission=”admin”>Add user-admin permission</a></h1>–>
<!–<h1> <a th:href=”@{/user/selectUser}” shiro:hasPermission=”guest”>User list--guest permission</a></h1>–>

 

The first configuration:

The main reason is that the relationship between paths and permissions is written dead in the shrio configuration class. The html is just a common way of writing. If it is not authorized, it will automatically jump to the /403 page in the shrio configuration class. The disadvantage is that there is a lot of writing, and the path needs to be standardized. Others think on their own

shiroFilterFactoryBean.setUnauthorizedUrl("/403");

Second configuration:

The difference is that all paths are intercepted in the shrio class, and the specific permission control is written in html, such as:

<!–<h1><a th:href=”@{/user/addUser}” shiro:hasPermission=”admin” >add user-admin permission</a></h1>–>
<!–<h1> <a th:href=”@{/user/selectUser}” shiro:hasPermission=”guest” >User list--guest permission</a></h1>–>

In this case, if there is no permission, the menu will not be displayed at all, only the menu with permission will be displayed. All you need to configure in the shrio configuration class to use the required plugins in thymeleaf. as follows:

@Bean public ShiroDialect shiroDialect() {     return new ShiroDialect(); }

The third configuration:

This configuration is also intercepted in the configuration class, and the page is also written in the same general way as the first one. The specific permission control is controlled in the controller, and some people say that it is best to configure it in the service, here it is configured in the controller. That is, several common annotations, such as:

@RequiresRoles("guest")

Based on SpringBoot, the following configuration needs to be added to the shrio configuration class:

/**
 * Enable shiro aop annotation support. Required
 * Use the proxy method; so you need to enable code support; otherwise, annotations such as @RequiresRoles will not take effect
 *
 * @param securityManager
 * @return
 */@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    return authorizationAttributeSourceAdvisor;}@Beanpublic DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
    DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
    advisorAutoProxyCreator.setProxyTargetClass(true);
    return advisorAutoProxyCreator;}

In this case, the menu without permission will also be displayed, which can be clicked, but will not jump to the configured unauthorized page. The background will report an error without permission, and there is no information in the foreground. The solution is to customize an exception handler and manually jump to the unauthorized page when an unauthorized exception is caught. The configuration is as follows:

Configure this exception handler in shrio

/**
 * Handle the jump problem when not authorized
 *
 * @return
 */@Beanpublic HandlerExceptionResolver solver() {
    HandlerExceptionResolver handlerExceptionResolver = new MyExceptionResolver();
    return handlerExceptionResolver;}

Define a new exception handler

public class MyExceptionResolver implements HandlerExceptionResolver{
    public ModelAndView resolveException(HttpServletRequest request,
                                         HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("==============Exception start=============");
        //If shiro is not authorized to operate, because shiro does not forward to the unauthorized url when operating auno and other parts
        if(ex instanceof AuthorizationException){// Jump to 403 unauthorized page here 
            ModelAndView mv = new ModelAndView( "redirect:/403" );
            return mv;
        }
        return null;
    }In this way, you can intercept the unauthorized exception and jump to 403.

This article only pastes part of the code, the full code download address is: shrio


Tags

Technical otaku

Sought technology together

Related Topic

1 Comments

author

lipitor oral & lt;a href="https://lipiws.top/"& gt;order lipitor 80mg online cheap& lt;/a& gt; atorvastatin 10mg tablet

Mhzvcc

2024-03-08

Leave a Reply

+