Abstract:

This blog post is the “Java seconds kill system combat series article” the fifth, in this blog post, we will integrate the authority authentication – authorization framework Shiro, to achieve user login authentication function, mainly used for: requiring users to buy goods or seconds kill goods, restrict users to login! It also filters specific urls (such as the url corresponding to the purchase request) (that is, requires the user to log in when accessing the specified URL).

Content:

Shiro is something you’ve probably heard of and even used. In brief, it is a very easy to use user identity authentication, authority authorization framework, can achieve user login authentication, authority, resource authorization, session management and other functions, in this second kill system, we will mainly use the framework to achieve user identity authentication and user login function. It is worth mentioning that the database table involved in the function module “Shiro realizes user login authentication” introduced in this blog is user information table user. The following is the actual code.

<! Shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>${shiro.version}</version>
</dependency>
 
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>${shiro.version}</version>
</dependency>
 
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>${shiro.version}</version>
</dependency>
 
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>${shiro.version}</version>
</dependency>Copy the code

(2) Followed by the development of user login, user login and user logout request function method in UserController, its complete source code is shown as follows:

@Autowired private Environment env; @requestMapping (value = {"/to/login"."/unauth"})
public String toLogin() {return "login"; } @requestMapping (value = value"/login",method = RequestMethod.POST)
public String login(@RequestParam String userName, @RequestParam String password, ModelMap modelMap){
    String errorMsg="";
    try {
        if(! SecurityUtils.getSubject().isAuthenticated()){ String newPsd=new Md5Hash(password,env.getProperty("shiro.encrypt.password.salt")).toString();
            UsernamePasswordToken token=new UsernamePasswordToken(userName,newPsd);
            SecurityUtils.getSubject().login(token);
        }
    }catch (UnknownAccountException e){
        errorMsg=e.getMessage();
        modelMap.addAttribute("userName",userName);
    }catch (DisabledAccountException e){
        errorMsg=e.getMessage();
        modelMap.addAttribute("userName",userName);
    }catch (IncorrectCredentialsException e){
        errorMsg=e.getMessage();
        modelMap.addAttribute("userName",userName);
    }catch (Exception e){
        errorMsg="User login exception, please contact administrator!";
        e.printStackTrace();
    }
    if (StringUtils.isBlank(errorMsg)){
        return "redirect:/index";
    }else{
        modelMap.addAttribute("errorMsg",errorMsg);
        return "login"; } // Exit @requestMapping (value =)"/logout")
public String logout(){
    SecurityUtils.getSubject().logout();
    return "login";
}Copy the code

Among them, when matching the user’s password, Md5Hash method, namely MD5 encryption method, is adopted here for matching (because the password field of the user in the user table of the database stores the encrypted string after MD5 encryption).

The content of the front-end page login.jsp is relatively simple and only requires the user to enter the most basic user name and password. The following figure shows part of the core source code of the page:




When the front end submits the user Login request, it will submit the user name and password to the login method corresponding to the back end UserController in the form of “Submit form”. The method first evaluates and verifies the most basic parameters. After the verification succeeds, Shiro’s built-in component Securityutils.getSubject ().login() method is invoked to perform the login operation, which is performed primarily in the “custom Realm doGetAuthenticationInfo method.” (3) The next step is to develop a custom Realm based on Shiro’s AuthorizingRealm and implement the user login authentication method, that is, doGetAuthenticationInfo() method. The complete source code is as follows:

/** * User-defined realm- For shiro authentication and authorization * @author :debug (SteadyJack) * @date: 2019/7/2 17:55 **/ public class CustomRealm extends AuthorizingRealm{ private static final Loggerlog= LoggerFactory.getLogger(CustomRealm.class); private static final Long sessionKeyTimeOut=3600_000L; @Autowired private UserMapper userMapper; // Override protected AuthorizationInfodoGetAuthorizationInfo(PrincipalCollection principalCollection) {
        returnnull; } // Authentication - login @override protected AuthenticationInfodoGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;
        String userName=token.getUsername();
        String password=String.valueOf(token.getPassword());
        log.info("Currently logged in username ={} Password ={}",userName,password);
 
        User user=userMapper.selectByUserName(userName);
        if (user==null){
            throw new UnknownAccountException("User name does not exist!");
        }
        if(! Objects.equals(1,user.getIsActive().intValue())){ throw new DisabledAccountException("Current user is disabled!");
        }
        if(! user.getPassword().equals(password)){ throw new IncorrectCredentialsException("Username and password do not match!");
        }
 
        SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(user.getUserName(),password,getName());
        setSession("uid",user.getId());
        returninfo; } /** * insert the key and the corresponding value into Shiro's session - finally to HttpSession management (if distributed session configuration, @param key * @param value */ private voidsetSession(String key,Object value){
        Session session=SecurityUtils.getSubject().getSession();
        if(session! =null){ session.setAttribute(key,value); session.setTimeout(sessionKeyTimeOut); }}}Copy the code

Among them, the userMapper. SelectByUserName (userName); It is used to query user entity information according to userName. The corresponding dynamic Sql is written as follows:

<! --> <select id="selectByUserName" resultType="com.debug.kill.model.entity.User">
  SELECT <include refid="Base_Column_List"/>
  FROM user
  WHERE user_name = #{userName}
</select>Copy the code

It is worth mentioning that when the user logs in successfully (i.e., the user name and password match the user table in the database), we will use Shiro’s Session mechanism to store the current user’s information to the server Session and cache it for a certain period of time! (In this case, 3600s, or 1 hour)! (4) Finally, we need to realize “how to automatically detect whether the user is in the login state when the user accesses the details of goods to be killed or buys goods or any business request that needs to be intercepted? If you have logged in, directly access the method logic corresponding to the service request. Otherwise, you need to go to the user login page and ask the user to log in. Based on such requirements, we need to use Shiro’s component ShiroFilterFactoryBean to implement “user login or not” determination, and use FilterChainDefinitionMap to intercept some link urls that need authorization, the complete source code is as follows:

* @author :debug (SteadyJack) * @date: 2019/7/2 17:54 **/ @Configuration public class ShiroConfig { @Bean public CustomRealmcustomRealm() {return new CustomRealm();
    }
 
    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        securityManager.setRealm(customRealm());
        securityManager.setRememberMeManager(null);
        return securityManager;
    }
 
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(){
        ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager());
        bean.setLoginUrl("/to/login");
        bean.setUnauthorizedUrl("/unauth"); // Intercepting some authorized link urls Map<String, String> filterChainDefinitionMap=new HashMap<>(); filterChainDefinitionMap.put("/to/login"."anon");
        filterChainDefinitionMap.put("/ * *"."anon");
 
        filterChainDefinitionMap.put("/kill/execute"."authc");
        filterChainDefinitionMap.put("/item/detail/*"."authc");
 
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        returnbean; }}Copy the code

Shiro’s ShiroFilterFactoryBean component will intercept urls =/kill/execute and URL=/item/detail/* when the user visits these urls, The system will ask the current user to log in (if the user has not logged in yet! If you have logged in, you can directly skip and enter the actual business module!

Shiro’s ShiroFilterFactoryBean component also sets the “Go to login page” and “Adjust the User if not authorized/Logged in page” links to/ to/login and /unauth! Shiro (5) at this point, integration framework front end code to realize the user login authentication of actual combat has been finished, the project/system to run out the tomcat server, open a browser to access the “” to kill the commodity list page, click on the” details “, at this time, because the user haven’t log in, it will jump to the user login page, As shown below:



Enter user name: debug, password: 123456, click “Login” button, login will be successful, and enter the “Details page”, as shown below:

After successful login, go back to the previous list page, that is, list page of Products to be killed, and click Details again. The details page of products to be killed will be directly displayed instead of user Login page, and the login state of the user will last for 1 hour. (This is done with Shiro’s Session).

Supplement:

1, at present, the overall construction of the second kill system and code combat has been completed, the complete source code database address can be downloaded here: gitee.com/steadyjack/… Remember Fork and Star!!