Java spike combat system integration series - Shiro implement user login authentication

Summary:

This post is "Java spike combat system a series of articles," the fifth chapter, in this post, we will integrate the certification authority - authorization framework Shiro, implement user login authentication function, mainly used for: require users to buy goods or when the commodity spike, limiting users to login! And for a particular url (corresponding to the request, such as buying url) to filter (i.e., when access to the specified url, requiring user login required).

content:

For Shiro, I believe you should have heard of a small partner, or even should be used! In simple terms, it is the user identity authentication, authorization privileges a good use of the framework, enabling user login authentication, permissions, resource authorization, session management and other functions, this spike in the system, we will adopt the framework to achieve the user authentication and user login functionality identity.

It is worth mentioning that, "Shiro implement user login authentication" this post describes the functional modules related to the database table for the user information table user, enter the following code is the actual link.

<!--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>

 

(2) followed by the development of the user to log in UserController controller, the user logon and the user logs out of the functions of the method corresponding to the request, the complete source code as follows:

@Autowired
private Environment env;
 
//跳到登录页
@RequestMapping(value = {"/to/login","/unauth"})
public String toLogin(){
    return "login";
}
 
//登录认证
@RequestMapping(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 ( "the userName" , the userName); 
    } the catch (Exception E) { 
        errorMsg = "user login exception, please contact the administrator!" ; 
        E.printStackTrace () ; 
    } 
    IF (StringUtils.isBlank (errorMsg)) {
         return "the redirect: / index" ; 
    } the else { 
        modelMap.addAttribute ( "errorMsg" , errorMsg);
         return "Login" ; 
    } 
} 
 
// Log
@RequestMapping(value = "/logout")
public String logout(){
    SecurityUtils.getSubject().logout();
    return "login";
}

 

 Wherein, when the user password matches, Md5Hash method we used here, i.e., MD5 encrypted manner matches (because it is encrypted using MD5 encrypted password string field stores the user table in the user database), the front end login.jsp content page is relatively simple, only requires the user to enter basic user name and password, as shown below for the core portion of the source code of the page:

 

Submit the current end "user login" request will be "Submit Form" will be presented in the form of a user name and password to log in method UserController controller corresponding to the rear end, the process will first check the basic parameters and determination, after the check is passed, Shiro calls built-in components SecurityUtils.getSubject (). login () method performs the login operation, wherein the log-in operation is mainly performed in the "custom Realm of doGetAuthenticationInfo method".

(3) Next it is based on Shiro's AuthorizingRealm, custom development Realm, and implement user login authentication method which, namely doGetAuthenticationInfo () method. Its complete source code as follows:

/**
 * 用户自定义的realm-用于shiro的认证、授权
 * @Author:debug (SteadyJack)
 * @Date: 2019/7/2 17:55
 **/
public class CustomRealm extends AuthorizingRealm{
    private static final Logger log= LoggerFactory.getLogger(CustomRealm.class);
 
    private static final Long sessionKeyTimeOut=3600_000L;
    @Autowired
    private UserMapper userMapper;
 
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
 
    //认证-登录
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;
        String userName=token.getUsername();
        String password=String.valueOf(token.getPassword());
        log.info("当前登录的用户名={} 密码={} ",userName,password);
 
        User user=userMapper.selectByUserName(userName);
        if (user==null){
            throw new UnknownAccountException("用户名不存在!");
        }
        if (!Objects.equals(1,user.getIsActive().intValue())){
            throw new DisabledAccountException("当前用户已被禁用!");
        }
        if (!user.getPassword().equals(password)){
            throw new IncorrectCredentialsException("用户名密码不匹配!");
        }
 
        SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(user.getUserName(),password,getName());
        setSession("uid",user.getId());
        return info;
    }
 
    /**
     * 将key与对应的value塞入shiro的session中-最终交给HttpSession进行管理(如果是分布式session配置,那么就是交给redis管理)
     * @param key
     * @param value
     */
    private void setSession(String key,Object value){
        Session session=SecurityUtils.getSubject().getSession();
        if (session!=null){
            session.setAttribute(key,value);
            session.setTimeout(sessionKeyTimeOut);
        }
    }
}

 

其中,userMapper.selectByUserName(userName);主要用于根据userName查询用户实体信息,其对应的动态Sql的写法如下所示:  

<!--根据用户名查询-->
<select id="selectByUserName" resultType="com.debug.kill.model.entity.User">
  SELECT <include refid="Base_Column_List"/>
  FROM user
  WHERE user_name = #{userName}
</select>

 

 值得一提的是,当用户登录成功时(即用户名和密码的取值跟数据库的user表相匹配),我们会借助Shiro的Session会话机制将当前用户的信息存储至服务器会话中,并缓存一定时间!(在这里是3600s,即1个小时)!


(4)最后是我们需要实现“用户在访问待秒杀商品详情或者抢购商品或者任何需要进行拦截的业务请求时,如何自动检测用户是否处于登录的状态?如果已经登录,则直接进入业务请求对应的方法逻辑,否则,需要前往用户登录页要求用户进行登录”。基于这样的需求,我们需要借助Shiro的组件ShiroFilterFactoryBean 实现“用户是否登录”的判断,以及借助FilterChainDefinitionMap拦截一些需要授权的链接URL,其完整的源代码如下所示:

/**
 * shiro的通用化配置
 * @Author:debug (SteadyJack)
 * @Date: 2019/7/2 17:54
 **/
@Configuration
public class ShiroConfig {
 
    @Bean
    public CustomRealm customRealm(){
        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");
       //对于一些授权的链接URL进行拦截
        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);
        return bean;
    }
 
}

 

 从上述该源代码中可以看出,Shiro的ShiroFilterFactoryBean组件将会对 URL=/kill/execute 和 URL=/item/detail/*  的链接进行拦截,即当用户访问这些URL时,系统会要求当前的用户进行登录(前提是用户还没登录的情况下!如果已经登录,则直接略过,进入实际的业务模块!),除此之外,Shiro的ShiroFilterFactoryBean组件还设定了 “前往登录页”和“用户没授权/没登录的前提下的调整页”的链接,分别是 /to/login 和 /unauth!


(5)至此,整合Shiro框架实现用户的登录认证的前后端代码实战已经完毕了,将项目/系统运行在外置的tomcat服务器中,打开浏览器即可访问进入“待秒杀商品的列表页”,点击“详情”,此时,由于用户还没登陆,故而将跳转至用户登录页,如下图所示:

 

 

输入用户名:debug,密码:123456,点击“登录”按钮,即可登录成功,并成功进入“详情页”,如下图所示:  

登录成功之后,再回到刚刚上一个列表页,即“待秒杀商品的列表页”,再次点击“详情”按钮,此时会直接进入“待秒杀商品的详情页”,而不会跳转至“用户登录页”,而且用户的登录态将会持续1个小时!(这是借助Shiro的Session的来实现的)。

补充:

1、目前,这一秒杀系统的整体构建与代码实战已经全部完成了,完整的源代码数据库地址可以来这里下载:https://gitee.com/steadyjack/SpringBoot-SecondKill 记得Fork跟Star啊!!!

2、实战期间有任何问题都可以留言或者与Debug联系、交流;QQ技术交流群:605610429,顺便关注一下Debug的技术微信公众号呗:

 

Guess you like

Origin www.cnblogs.com/SteadyJack/p/11220547.html