How quickly learn in just 30 minutes Shiro

In this part summarizes the most from Zhang Kaitao of "follow me Shiro" Original Address: http: //jinnianshilongnian.iteye.com/blog/2018936

I did not read all, just choose the knowledge part of the urgent need for me to use in the project to be learning. And for most of the first contact with Shiro students, the mastery of these should be enough.

First, architecture

To learn how to use Shiro must start talking about its architecture, as a security framework Shiro design is quite sophisticated. Shiro application not dependent on any container which may be used in JavaSE. But the most common environment or JavaEE. As a user, for example the following:

(1) using the user's login information to create a token

UsernamePasswordToken token = new UsernamePasswordToken(username, password);

token can be understood as a user token, the login process is abstracted as Shiro verify whether the token has legal status and related privileges.

(2) the implementation of the landing operation

SecurityUtils.setSecurityManager(securityManager); // 注入SecurityManager
Subject subject = SecurityUtils.getSubject (); // get the Subject singletons
subject.login (token); // Log

Shiro is a core part of SecurityManager, which is responsible for security authentication and authorization. Shiro itself has achieved all of the details, the user can complete it as a black box to use. SecurityUtils object is essentially similar to a factory in Spring ApplicationContext. Subject is more difficult to understand for beginners objects, many people think it can be equated to User, it is not. Subject Chinese translation: the project, and properly understood is precisely the case. It is an abstract concept that you currently need designed by Shiro protection projects. By token (token) and project (subject) login (login) relations, Shiro ensure the overall safety of the project.

(3) determine the user

Shiro held by itself can not know whether a token of the legitimate user, because in addition to the designer of the project, I am afraid no one can know. So Realm is one of the few in the entire framework modules themselves must be implemented by the designer, of course, Shiro offers a variety of ways to achieve, this article only describes the most common and most important one implementation - database queries.

(4) two important English

The first obstacle I encountered in the process of learning Shiro of these two objects is the English name: AuthorizationInfo, AuthenticationInfo. I do not doubt their own eyes, long like they do, not only like a long, even very similar meaning.

Before explaining they must first describe Shiro define security for users: most operating systems. User roles and permissions have two basic properties. For example, my Windows logon name is learnhow, its role is administrator, and the administrator has all system privileges. Such learnhow naturally have all the system privileges. Then others need to log in to my computer how to do, I can open a guest role, can not provide any correct user name and password unknown user can log in by guest, and the system is extremely limited for guest roles open permissions.

Similarly, Shiro constraints on the user in such a way also used. AuthenticationInfo represents the user's role information collection, AuthorizationInfo collection of information on behalf of the authority roles. Thus, when the designer of the project in one of url paths for allowing only a role or have some privileges to access control constraints of time, Shiro can be judged by two or more objects. Here, it may also be more confused. Do not worry, we will naturally continue to look back and understand.

Second, the realization Realm

How to achieve Realm is the highlight of this article, it is more troublesome part. Here you will come into contact with several fresh concepts: caching, hashing algorithm, encryption algorithm. Because this is not devoted to these concepts, so here merely to make a few better ideas that can help you better understand Shiro can be.

(1) cache mechanism

Ehcache is a lot of Java caching framework used in the project, Hibernate is one of them. It is the nature of the original data can only be stored in memory by the algorithm stored to the hard disk, and then turn out on demand. You can Ehcache understood as a Map <String, Object> object, put saving the object, and then get through to retrieve objects.

Copy the code
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="shirocache">
    <diskStore path="java.io.tmpdir" />
    
    <cache name="passwordRetryCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>
</ehcache>
Copy the code

These are the basic configuration ehcache.xml file, timeToLiveSeconds is the largest cache of survival time, timeToIdleSeconds maximum idle time for the cache, when eternal is false ttl and tti before they can take effect. More meaning you can configure to query online.

(2) Hash algorithms and encryption algorithms

md5 hash algorithm will be used herein, this article does not involve encryption algorithm. And encrypting the hash Object essentially a string into a string of meaningless, after the object is different from the hash can not be undone, a one-way process. For example, encryption of passwords is usually using a hashing algorithm, so the user only if you forget your password and can not get the original password by modifying. But for the encrypted information is formal encryption algorithm, encrypted information can be decrypted by a secret key and restore.

(3) User Registration

Please note that, although the security problems we've been talking about user login, but when it comes to user login user is first registered. How to ensure that user registration information is not lost, non-disclosure is the focus of the project design.

Copy the code
public class PasswordHelper {
    private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
    private String algorithmName = "md5";
    private final int hashIterations = 2;

    public void encryptPassword(User user) {
        // User object contains the basic fields Username and Password
        user.setSalt(randomNumberGenerator.nextBytes().toHex());
        // user's registered password hashing algorithm to replace after an irreversible new password is saved into the data, hashing process uses salt
        String newPassword = new SimpleHash(algorithmName, user.getPassword(),
                ByteSource.Util.bytes (user.getCredentialsSalt ()), Takahashi Removing tions) .toHex ();
        user.setPassword(newPassword);
    }
}
Copy the code

If you do not know what Hash called salt process can be ignored, as long as you know the password stored in the database according to a new string is entered when the user registration password generated by it. After replacing passwords hashed password when user registration, then the User saved to the database. UserService rest of the work left to be processed.

So this brought a new problem, since the hash algorithm is not restored when a password when users log on using the original registration, how should we judge? The answer is needed again in the same hashing algorithm once the user password, and then compare the string stored in the same database.

(4) match

CredentialsMatcher is an interface function is used to match the saved token and database users log in with the user information matches. Of course, its function is not only that. This article is to introduce a class that implements this interface: HashedCredentialsMatcher

Copy the code
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
    // declare a cache interface, this interface is part of Shiro cache management, and its concrete realization can be injected through the outer container
    private Cache<String, AtomicInteger> passwordRetryCache;

    public RetryLimitHashedCredentialsMatcher (CacheManager CacheManager) {
        passwordRetryCache = cacheManager.getCache("passwordRetryCache");
    }

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        String username = (String) token.getPrincipal();
        AtomicInteger retryCount = passwordRetryCache.get(username);
        if (retryCount == null) {
            retryCount = new AtomicInteger(0);
            passwordRetryCache.put(username, retryCount);
        }
        // customize a verification process: When the user continues to enter the wrong password more than 5 times prevents users from logging period of time
        if (retryCount.incrementAndGet() > 5) {
            throw new ExcessiveAttemptsException();
        }
        boolean match = super.doCredentialsMatch(token, info);
        if (match) {
            passwordRetryCache.remove(username);
        }
        return match;
    }
}
Copy the code

You can see, this is only achieved in the designer adds a judge does not allow continuous error log. Or to the real process of matching its direct parent to complete. Continuous login errors of judgment relies Ehcache cache to achieve. Obviously match returns true for a successful match.

(5) to obtain the user's role and permissions information

Having said that our only focus Realm, if you understand Shiro for registered users matching and encryption of the whole process, to achieve a true understanding of the Realm but relatively simple. We have to return two very similar objects AuthorizationInfo and AuthenticationInfo mentioned above. Because local Realm is to provide these two objects.

Copy the code
public class UserRealm extends AuthorizingRealm {
    // role information and authority information corresponding to the user are stored in the database, the data acquired by UserService
    private UserService userService = new UserServiceImpl();

    /**
     * Provide user information return rights information
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        // query role of the current user owns the user name
        Set<Role> roles = userService.findRoles(username);
        Set<String> roleNames = new HashSet<String>();
        for (Role role : roles) {
            roleNames.add (role.getRole ());
        }
        // role names will be provided to info
        authorizationInfo.setRoles(roleNames);
        // query the current user permissions based on user name
        Set<Permission> permissions = userService.findPermissions(username);
        Set<String> permissionNames = new HashSet<String>();
        for (Permission permission : permissions) {
            permissionNames.add(permission.getPermission());
        }
        // permission to provide the name of info
        authorizationInfo.setStringPermissions(permissionNames);

        return authorizationInfo;
    }

    /**
     * Provide account information returns authentication information
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        User user = userService.findByUsername(username);
        if (user == null) {
            // username does not exist throw an exception
            throw new UnknownAccountException();
        }
        if (user.getLocked() == 0) {
            // user is locked thrown administrator
            throw new LockedAccountException();
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(),
                user.getPassword(), ByteSource.Util.bytes(user.getCredentialsSalt()), getName());
        return authenticationInfo;
    }
}
Copy the code

The relationship of design ideas before Shiro, user and role-many relationship between the role and authority is many to many. Need therefore to establish the database five tables are user table (stores the user name, password, salt, etc.), the role of table (Role Name, Description, etc.), permission table (permission name, Description, etc.), user - role the corresponding intermediate table (role ID and the user ID as the primary key), the role - the intermediate permission correspondence table (in the role ID and the authorization ID as the primary key). Specific dao and service implementations article does not provide. In short conclusion is that, Shiro required if the username and password to log in first determine the legitimate user, then authorized to legitimate users. And this process is the Realm of the implementation process.

(6) Session

The user's single sign-on is the first session, Shiro also can replace container such as Tomcat management session. The purpose is that when a user stays on a page for a long time no action when, once again access to any link will be redirected to the login page asked to re-enter your user name and password without the need for programmers to keep in the Servlet determine whether the Session User object contains. Another use is enabled Shiro session management can take different sessions for different processing modules. Taobao, for example, after Taobao registered users can choose to remember the username and password. After the visit it again without landing. But if you want to access pay-or-cart and other links still requires the user to confirm the identity. Of course, Shiro can also create Session use the container to achieve the most.

Third, integration with SpringMVC

With registered modules support module and Realm, here is how to integrate development and SpringMVC. Integrated framework had experienced students must know, so-called integrated basically a bunch of xml file configuration, Shiro no exception.

(1) Configuration frontend filters

Let me talk a digression, Filter is a filter, interceptor interceptor. Based on the former callback function, you must rely on container supports. Because of the need container assembled the entire FilterChain and one by one call. The latter agent-based implementation, belong to the category of AOP.

If you want to use Shiro must first be configured in the web.xml file in the WEB environment

Copy the code
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">
    <display-name>Shiro_Project</display-name>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <! - will Shiro Spring configuration file to initialize the listener ->
        <param-value>classpath:spring.xml,classpath:spring-shiro-web.xml</param-value>
    </context-param>
    <context-param>
        <param-name>log4jConfigLoaction</param-name>
        <param-value>classpath:log4j.properties</param-value>
    </context-param>
    <-! Shiro Configuration Start ->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <async-supported>true</async-supported>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <-! Shiro end configuration ->
</web-app>
Copy the code

Spring configuration familiar students can look at the focus part of a green word comment, here it is the key to the entry into force of Shiro. Because project management through the Spring, so all the configuration guidelines are handed over to Spring. Spring DelegatingFilterProxy function is to inform all Filter to ShiroFilter management.

Then spring-shiro-web.xml configuration file in the classpath

Copy the code
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans    
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd    
                        http://www.springframework.org/schema/context    
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd    
                        http://www.springframework.org/schema/mvc    
                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

    <! - Cache Manager uses Ehcache achieve ->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml" />
    </bean>

    <! - Document matcher ->
    <bean id="credentialsMatcher" class="utils.RetryLimitHashedCredentialsMatcher">
        <constructor-arg ref="cacheManager" />
        <property name="hashAlgorithmName" value="md5" />
        <property name="hashIterations" value="2" />
        <property name="storedCredentialsHexEncoded" value="true" />
    </bean>

    <-! Realm implementation ->
    <bean id="userRealm" class="utils.UserRealm">
        <property name="credentialsMatcher" ref="credentialsMatcher" />
    </bean>

    <! - Security Manager ->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="userRealm" />
    </bean>

    <-! Shiro's Web Filter ->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="/" />
        <property name="unauthorizedUrl" value="/" />
        <property name="filterChainDefinitions">
            <value>
                /authc/admin = roles[admin]
                /authc/** = authc
                /** = anon
            </value>
        </property>
    </bean>

    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
</beans>
Copy the code

Note filterChainDefinitions filter path is arranged for sequential, when the matching entry is found in the container will not continue to search. Thus the path to be on the back with wildcards. Meaning three configurations are: / authc / admin requires the user to be useful admin privileges, / authc / ** Users must log in to access, / ** all the other paths can be accessed by anyone.

Having said that, we must be concerned about in the end how to write the code to log in Spring after the introduction of Shiro it.

Copy the code
@Controller
public class LoginController {
    @Autowired
    private UserService userService;

    @RequestMapping("login")
    public ModelAndView login(@RequestParam("username") String username, @RequestParam("password") String password) {
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
        } catch (IncorrectCredentialsException ice) {
            // capture passwords error exception
            ModelAndView mv = new ModelAndView("error");
            mv.addObject("message", "password error!");
            return mv;
        } catch (UnknownAccountException uae) {
            // capture unknown user name abnormalities
            ModelAndView mv = new ModelAndView("error");
            mv.addObject("message", "username error!");
            return mv;
        } catch (ExcessiveAttemptsException eae) {
            // capture too many errors to log anomaly
            ModelAndView mv = new ModelAndView("error");
            mv.addObject("message", "times error");
            return mv;
        }
        User user = userService.findByUsername(username);
        subject.getSession().setAttribute("user", user);
        return new ModelAndView("success");
    }
}
Copy the code

After registration is completed, the information is saved into the current user Session. This Session by session object Shiro managed to get still must pass Shiro. User object does not exist in the traditional Session.

Copy the code
@Controller
@RequestMapping("authc")
public class AuthcController {
    // / authc / ** = authc by any user can access the login form
    @RequestMapping("anyuser")
    ModelAndView public anyuser () {
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getSession().getAttribute("user");
        System.out.println(user);
        return new ModelAndView("inner");
    }

    // / authc / admin = user [admin] admin role only with the user can access, otherwise the request will be redirected to the login screen
    @RequestMapping("admin")
    ModelAndView public admin () {
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getSession().getAttribute("user");
        System.out.println(user);
        return new ModelAndView("inner");
    }
}
Copy the code

IV Summary

Shiro is a function of the framework is complete, it is also very easy to use, but in order to make good use there is quite difficult. Complete source code of the project is not provided here, we need to exchange students can give me a message or direct access to Zhang Kaitao blog. If you feel I can write, I also want to give some feedback.

V Recommended

This is a learning shiro site, want the system to learn the students can go .

Sixth, sell melon

Shiro want to know Springboot integration of small partners can be found in another article text: " 30 minutes to understand Springboot integration Shiro "

Guess you like

Origin www.cnblogs.com/DuJiu/p/12563447.html