Spring boot introduction: integration of Shiro to achieve login authentication and rights management

This article is followed by the last blog: Introduction to Spring boot (3): Spring boot integration combined with AdminLTE(Freemarker), using generation to automatically generate code, using DataTable and Page Helper for paging display . According to the previous blog, we can build a simple Spring Boot system. This blog continues to transform this system. It mainly integrates Shiro authorization framework, Shiro section, and my previous blog.( Authentication and Shiro Security Framework ) There are introductions. There are no redundant introductions here.

This series of blogs is part of practice, focusing on the process of code and system building. If you encounter professional terms, you can find their meaning by yourself.

1.Shiro configuration class

So far, the system has been mainly used in three configuration classes, all related to Shiro. Later, with the expansion of the project, the number of configuration files will increase.

 

  • FreeMarker Config: For the configuration of FreeMarker page display, regarding Shiro section, a shared variable is set for Shiro tag. If this variable is not set, the FreeMarker page will not recognize Shiro tag. Its main code is as follows:
1 configuration.setSharedVariable("shiro", new ShiroTags());
  • MShiroFilterFactoryBean: Setting up filters, of course, can also configure filters in Config files. The disadvantage is that session read and update access time are done in each request, which increases the processing load in the case of cluster deployment session sharing. Distributed will be used later in this project, so the filter and Config configuration file will be separated directly to improve efficiency.
 1 private final class MSpringShiroFilter extends AbstractShiroFilter {
 2         protected MSpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
 3             super();
 4             if (webSecurityManager == null) {
 5                 throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
 6             }
 7             setSecurityManager(webSecurityManager);
 8             if (resolver != null) {
 9                 setFilterChainResolver(resolver);
10             }
11         }
12 
13         @Override
14         protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse,
15                                         FilterChain chain) throws ServletException, IOException {
16             HttpServletRequest request = (HttpServletRequest) servletRequest;
17             String str = request.getRequestURI().toLowerCase();
18             boolean flag = true;
19             int idx = 0;
20             if ((idx = str.indexOf(".")) > 0) {
21                 str = str.substring(idx);
22                 if (ignoreExt.contains(str.toLowerCase()))
23                     flag = false;
24             }
25             if (flag) {
26                 super.doFilterInternal(servletRequest, servletResponse, chain);
27             } else {
28                 chain.doFilter(servletRequest, servletResponse);
29             }
30         }
31 
32     }
  • Shiro Configuration: General configuration file, which is Shiro's basic general configuration file. As long as it is integrated with Shiro, it must have this file. It mainly configures information related to Shiro's login authentication. Its code is as follows:
  1 /**
  2      * Set up shiro's cache, and the cache parameters are all configured in the xml file
  3      * @return
  4      */
  5     @Bean
  6     public EhCacheManager getEhCacheManager() {
  7         EhCacheManager em = new EhCacheManager();
  8         em.setCacheManagerConfigFile("classpath:ehcache/ehcache-shiro.xml");  
  9         return em;  
 10     }
 11     /**
 12      * Credential matcher
 13      * (Because our password checking was handed over to Shiro's Simple Authentication Info for processing
 14      *  So we need to modify the code in doGetAuthentication Info.
 15      * )
 16      * @return
 17      */
 18     @Bean
 19     public HashedCredentialsMatcher hashedCredentialsMatcher(){
 20        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
 21        hashedCredentialsMatcher.setHashAlgorithmName("md5");//hash:Use here MD5 algorithm;
 22        hashedCredentialsMatcher.setHashIterations(1);//The number of hashes, such as two, equals md5(md5(""));
 23        return hashedCredentialsMatcher;
 24     }
 25     /**
 26      * 
 27      * Master file
 28      */
 29     @Bean(name = "myShiroRealm")
 30     public UserRealm myShiroRealm(EhCacheManager cacheManager) {
 31         UserRealm realm = new UserRealm(); 
 32         realm.setCacheManager(cacheManager);
 33         realm.setCredentialsMatcher(hashedCredentialsMatcher());
 34         return realm;
 35     }
 36    //Conversation ID generator
 37     @Bean(name = "sessionIdGenerator")
 38     public JavaUuidSessionIdGenerator javaUuidSessionIdGenerator(){
 39         JavaUuidSessionIdGenerator javaUuidSessionIdGenerator = new JavaUuidSessionIdGenerator();
 40         return javaUuidSessionIdGenerator;
 41     }
 42     @Bean(name = "sessionIdCookie")
 43     public SimpleCookie getSessionIdCookie(){
 44         SimpleCookie sessionIdCookie = new SimpleCookie("sid");
 45         sessionIdCookie.setHttpOnly(true);
 46         sessionIdCookie.setMaxAge(-1);
 47         return sessionIdCookie;
 48         
 49     }
 50     /*<!-- Conversation DAO - >*/
 51     @Bean(name = "sessionDAO")
 52     public EnterpriseCacheSessionDAO enterpriseCacheSessionDAO(){
 53         EnterpriseCacheSessionDAO sessionDao = new EnterpriseCacheSessionDAO();
 54         sessionDao.setSessionIdGenerator(javaUuidSessionIdGenerator());
 55         sessionDao.setActiveSessionsCacheName("shiro-activeSessionCache");
 56         return sessionDao;
 57     }
 58     @Bean(name = "sessionValidationScheduler")
 59     public ExecutorServiceSessionValidationScheduler getExecutorServiceSessionValidationScheduler() {
 60         ExecutorServiceSessionValidationScheduler scheduler = new ExecutorServiceSessionValidationScheduler();
 61         scheduler.setInterval(1800000);
 62         return scheduler;
 63     }
 64     @Bean(name = "sessionManager")
 65     public DefaultWebSessionManager sessionManager(EnterpriseCacheSessionDAO sessionDAO){
 66         DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
 67         sessionManager.setGlobalSessionTimeout(1800000);
 68         sessionManager.setDeleteInvalidSessions(true);
 69         sessionManager.setSessionValidationSchedulerEnabled(true);
 70         sessionManager.setSessionValidationScheduler(getExecutorServiceSessionValidationScheduler());
 71         sessionManager.setSessionDAO(sessionDAO);
 72         sessionManager.setSessionIdCookieEnabled(true);
 73         sessionManager.setSessionIdCookie(getSessionIdCookie());
 74         return sessionManager;
 75     }
 76     @Bean(name = "lifecycleBeanPostProcessor")
 77     public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
 78         return new LifecycleBeanPostProcessor();
 79     }
 80     @Bean
 81     public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
 82         DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
 83         daap.setProxyTargetClass(true);
 84         return daap;
 85     }
 86     @Bean(name = "securityManager")
 87     public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm myShiroRealm, DefaultWebSessionManager sessionManager) {
 88         DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
 89         dwsm.setRealm(myShiroRealm);
 90 //      <!-- User authorization/Authentication information Cache, Use EhCache cache --> 
 91         dwsm.setCacheManager(getEhCacheManager());
 92         dwsm.setSessionManager(sessionManager);
 93         return dwsm;
 94     }
 95     /**
 96      *  Open shiro aop annotation support.
 97      *  Use proxy mode; so code support needs to be turned on;
 98      * @param securityManager
 99      * @return
100      */
101     @Bean
102     public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
103         AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
104         aasa.setSecurityManager(securityManager);
105         return aasa;
106     }
107     /**
108      * ShiroFilter<br/>
109      * Note that Student Service and IScoreDao in the parameters are just examples, because we can get the relevant objects accessing the database in this way.
110      * Then read the database configuration and configure it into the access rules of shiroFilterFactoryBean. In a real project, use your own Service to handle business logic.
111      *
112      */
113     @Bean(name = "shiroFilter")
114     public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
115         ShiroFilterFactoryBean shiroFilterFactoryBean = new MShiroFilterFactoryBean();
116         // Must set SecurityManager  
117         shiroFilterFactoryBean.setSecurityManager(securityManager);
118         // If you do not set the default, it will automatically find Web Project Root Catalogue"/login.jsp"page
119         shiroFilterFactoryBean.setLoginUrl("/login");
120         // Connections to jump after successful login
121         shiroFilterFactoryBean.setSuccessUrl("/certification");
122         //shiroFilterFactoryBean.setSuccessUrl("/index");
123         shiroFilterFactoryBean.setUnauthorizedUrl("/403");
124         loadShiroFilterChain(shiroFilterFactoryBean);
125         return shiroFilterFactoryBean;
126     }
127     /**
128      * Load shiroFilter privilege control rules (read from database and configure)
129      *
130      */
131     private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean){
132         /////////////////////// The following rules are best configured in the configuration file ///////////////////////
133         Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
134         // authc: The page under this filter must be validated before it can be accessed. It is Shiro A built-in interceptor org.apache.shiro.web.filter.authc.FormAuthenticationFilter
135         filterChainDefinitionMap.put("/login", "authc");
136         filterChainDefinitionMap.put("/logout", "logout");
137         // anon: Its corresponding filter is empty inside.,You've done nothing at all?
138         logger.info("##################Read permission rules from the database and load them to shiroFilter in##################");
139 //        filterChainDefinitionMap.put("/user/edit/**", "authc,perms[user:edit]");// For testing purposes, fixed write-to-death values can also be read from databases or other configurations.
140         shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
141     }

2. Login Authentication and Authority Management

Mainly rewrite Realm domain, complete authority authentication and authority management:

 1     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
 2         //If no permission validation is done, all you need to do here is return null that will do
 3         SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
 4         String userName = (String) principals.getPrimaryPrincipal();
 5         Result<TUser> list = userService.getUserByUsername(userName);
 6         if(list.isStatus()) {
 7             //Get the role that the user belongs to
 8             Result<List<TRole>> resultRole = roleService.getRoleByUserId(list.getResultData().getUserId());
 9             if(resultRole.isStatus()) {
10                 HashSet<String> role = new HashSet<String>();
11                 for(TRole tRole : resultRole.getResultData()) {
12                     role.add(tRole.getRoleId()+"");
13                 }
14                 //Get the permissions that the role has
15                 Result<List<TPermission>> resultPermission = permissionService.getPermissionsByRoleId(role);
16                 if(resultPermission.isStatus()) {
17                     HashSet<String> permissions = new HashSet<String>();
18                     for(TPermission tPermission : resultPermission.getResultData()) {
19                         permissions.add(tPermission.getPermissionsValue());
20                     }
21                     System.out.println("Jurisdiction:"+permissions);
22                     authorizationInfo.setStringPermissions(permissions);
23                 }
24             }
25         }
26         //return null;
27         return authorizationInfo;
28     }
29 
30     @Override
31     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
32         //Authentication login
33         String username = (String) authenticationToken.getPrincipal();
34         //String password = new String((char[]) authenticationToken.getCredentials());
35         Result<TUser> result = userService.getUserByUsername(username);
36         if (result.isStatus()) {
37             TUser user = result.getResultData();
38             return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
39         }
40         //return new SimpleAuthenticationInfo(user., "123456", getName());
41         return null;
42     }
43 }

2.1. Login Authentication

First, create a front-end login interface and make a simple login Form form.

Click on login to send a request in the background. It must be a Post request. Otherwise Shiro cannot recognize it. The authentication part is mainly completed in Ream. A new class is created, inherits AuthorizingRealm, and then rewrites the doGetAuthentication Info method:

 
We just need to search the user name on the interface to find the relevant information stored in the database. The specific authentication is done by Shiro itself. We just need to pass in a new Simple Authentication Info (user. getUsername (), user. getPassword (), getName ()) stored in the database. We can redefine the password comparator ourselves. The cipher comparator is written in many ways. Authentication and Shiro Security Framework In this project, the password comparator is rewritten by configuring. Refer to the Shiro Configuration configuration class for the specific code.

 

In the specific Login method, it is enough to write some exceptions of login failure. The main user will save the result of login failure into Session and display it on the page:
 1     @RequestMapping(value = "/login", method = RequestMethod.POST)
 2     public String postLogin(RedirectAttributes redirectAttributes, HttpServletRequest request, HttpSession session) {
 3         // Logon failure from request Get in shiro Processing exception information.
 4         // shiroLoginFailure:Namely shiro The full class name of the exception class.
 5         String exception = (String) request.getAttribute("shiroLoginFailure");
 6 
 7         System.out.println("exception=" + exception);
 8         String msg = "";
 9         if (exception != null) {
10             if (UnknownAccountException.class.getName().equals(exception)) {
11                 System.out.println("UnknownAccountException -- > No account exists:");
12                 msg = "Users do not exist!";
13             } else if (IncorrectCredentialsException.class.getName().equals(exception)) {
14                 System.out.println("IncorrectCredentialsException -- > The password is incorrect:");
15                 msg = "The password is incorrect!";
16             } else if ("kaptchaValidateFailed".equals(exception)) {
17                 System.out.println("kaptchaValidateFailed -- > Verification code error");
18                 msg = "Verification code error!";
19             } else {
20                 //msg = "else >> "+exception;
21                 msg = "The password is incorrect!";
22                 System.out.println("else -- >" + exception);
23             }
24         }
25         redirectAttributes.addFlashAttribute("msg", msg);
26         session.setAttribute("msg", msg);
27         //return redirect("/login");
28         return "redirect:login";
29         //return msg;
30     }

At this time, the login authentication department has completed: one page + two background functions (one authentication function + one Login function)

2.2. Authority Management

Generally speaking, the privilege management only needs to add Shiro's privilege label to the interface. It can use the role label or the privilege label. Generally, the two labels are used together, and the effect is the best <@shiro.hasPermission name="xtgl-yhgl:read"> <@shiro.haslen name="xtgl-yhgl:read">

 

In addition, in Realm, the business logic of permission authentication needs to be rewritten. Usually, the user's role is found by the user ID, and then the role's permission is found by the role ID, and the role or permission is written into Shiro: authorization Info. setString Permissions (permissions); authorization Info. setRoles (roles);

This project also completes the privilege management through this logic.

The two screenshots above represent a function.

So far, the authority authentication of Spring Boot integrated Shiro framework has been built, which can realize simple authority management.

3. Additional Documents

New files added to Shiro section compared with the previous blog

 

 



Keywords: Java Shiro Database Session Spring

Added by XZer0cool on Sat, 18 May 2019 06:38:44 +0300