美文网首页SpringBoot
springboot整合shiro

springboot整合shiro

作者: 东方舵手 | 来源:发表于2018-08-17 23:17 被阅读206次

    1. pom.xml文件引入依赖

    <properties>
        <shiro.version>1.3.2</shiro.version>
    </properties>
    
    <!-- 集成shiro -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>${shiro.version}</version>
    </dependency>
    <!-- shiro缓存 -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-ehcache</artifactId>
        <version>${shiro.version}</version>
    </dependency>
    

    2. 创建shiro配置类

    import com.qfedu.rongzaiboot.shiro.UserRealm;
    import org.apache.shiro.cache.ehcache.EhCacheManager;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.session.mgt.SessionManager;
    import org.apache.shiro.spring.LifecycleBeanPostProcessor;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * shiro配置
     */
    @Configuration
    public class ShiroConfig {
    
        /**
         * session管理器
         * @return
         */
        @Bean(name = "sessionManager")
        public SessionManager sessionManager(){
            DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            //设置session的过期时间为1小时,(默认时间时30分钟)
            sessionManager.setGlobalSessionTimeout(60*60*1000);
            //开启扫描session线程,清理超时会话
            sessionManager.setSessionValidationSchedulerEnabled(true);
            //禁用了url重写 去掉URL中的JSESSIONID
            sessionManager.setSessionIdUrlRewritingEnabled(false);//默认true
            return sessionManager;
        }
    
    
        /**
         * 创建SecurityManager
         */
        @Bean
        public SecurityManager securityManager(UserRealm userRealm, SessionManager sessionManager){
            //密码加密规则
            /*HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
            credentialsMatcher.setHashAlgorithmName("md5");
            credentialsMatcher.setHashIterations(1024);
            //credentialsMatcher.setHashSalted(true);
            userRealm.setCredentialsMatcher(credentialsMatcher);*/
    
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(userRealm);
            securityManager.setSessionManager(sessionManager);
            securityManager.setCacheManager(ehCacheManager());
            return securityManager;
        }
    
        /**
         * 创建shiroFilter过滤器
         * @param securityManager
         * @return
         */
        @Bean
        public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
            //anon:它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main -->
            //authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
            ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
            shiroFilter.setSecurityManager(securityManager);
            shiroFilter.setLoginUrl("/login.html");//没有认证时跳转到的登陆页
            shiroFilter.setSuccessUrl("/index.html");//认证成功跳转到主页
            shiroFilter.setUnauthorizedUrl("/unauthorized.json");//未授权时的跳转链接
    
            Map<String,String> filterMap = new LinkedHashMap<>();
            filterMap.put("/public/**","anon"); //放行静态资源的路径
            filterMap.put("/login.html","anon");
            filterMap.put("/sys/login","anon");
            filterMap.put("/captcha.jpg","anon");//验证码的图片
            //filterMap.put("/**","authc");//authc经过认证才能访问
    
            //角色验证 具有admin角色的用户可以访问
            //filterMap.put("/sys/menu/del","roles[admin]");
            //权限验证 具有perms[sys:menu:update]可以访问
            //filterMap.put("/sys/menu/update","perms[sys:menu:update]");
    
            filterMap.put("/**","user");//通过记住我访问
    
            shiroFilter.setFilterChainDefinitionMap(filterMap);
            return shiroFilter;
        }
    
        /**
         * 创建shiro缓存
         * @return
         */
        @Bean
        public EhCacheManager ehCacheManager(){
            EhCacheManager ehCacheManager = new EhCacheManager();
            ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
            return ehCacheManager;
        }
    
        /**
         * ShiroConfig配置文件中开启注解
         * 配置三个bean:
         * lifecycleBeanPostProcessor
         * defaultAdvisorAutoProxyCreator
         * authorizationAttributeSourceAdvisor
         */
    
        @Bean
        public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
            return new LifecycleBeanPostProcessor();
        }
    
        @Bean
        public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
            DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
            proxyCreator.setProxyTargetClass(true);
            return proxyCreator;
        }
    
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
            AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
            advisor.setSecurityManager(securityManager);
            return advisor;
        }
    
    }
    

    2.1 创建自定义Realm类 实现认证和授权

    package com.qfedu.rongzaiboot.shiro;
    
    import com.qfedu.rongzaiboot.entity.SysUser;
    import com.qfedu.rongzaiboot.service.SysUserService;
    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 org.springframework.stereotype.Component;
    
    import java.util.Set;
    
    @Component
    public class UserRealm extends AuthorizingRealm {
    
        @Autowired
        private SysUserService sysUserService;
    
        /**
         * 认证
         * @param token
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            System.out.println("认证........");
    
            String usernameInput = (String) token.getPrincipal();
            String passwordInput = new String((char[])token.getCredentials());
            //查询用户是否存在
            SysUser user = sysUserService.queryByUserName(usernameInput);
            if(user == null){
                throw new UnknownAccountException("账号或密码不正确");
            }
    
            //数据库中获取的用户名和密码
            String username = user.getUsername();
            String password = user.getPassword();
    
            //判断密码是否正确
            if(!passwordInput.equals(user.getPassword())){
                throw new IncorrectCredentialsException("账号或密码不正确");
            }
    
            //判断用户账号是否被锁定
            if (user.getStatus() == 0){
                throw new LockedAccountException("账号已被锁定,请联系管理员");
            }
    
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, this.getName());
    
            return info;
        }
    
        /**
         * 授权
         * @param principals
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            System.out.println("授权........");
    
            SysUser user = (SysUser) principals.getPrimaryPrincipal();
            Set<String> userPermissions = sysUserService.getUserPermissions(user.getUserId());
    
            SimpleAuthorizationInfo authorInfo = new SimpleAuthorizationInfo();
            authorInfo.addStringPermissions(userPermissions);
    
            //角色授权
            /*List<String> roleList = Arrays.asList("admin");
            authorInfo.addRoles(roleList);*/
    
            //资源授权
            /*List<String> permList = Arrays.asList("sys:menu:update");
            authorInfo.addStringPermissions(permList);*/
    
            return authorInfo;
        }
    }
    

    3. 创建service和dao

    3.1 SysUserService接口和SysUserServiceImpl实现类

    //接口
    public interface SysUserService {
    
        /**
         * 根据用户名,查询系统用户
         */
        SysUser queryByUserName(String username);
    }
    
    //实现类
    @Service
    public class SysUserServiceImpl implements SysUserService {
    
        @Autowired
        private SysUserMapper sysUserMapper;
        @Autowired
        private SysMenuMapper sysMenuMapper;
    
        @Override
        public SysUser queryByUserName(String username) {
            return sysUserMapper.queryByUserName(username);
        }
    
        @Override
        public Set<String> getUserPermissions(Long userId) {
            List<String> permsList = null;
    
            //超级管理员
            if (userId == 1) {
                List<SysMenu> menuList = sysMenuMapper.queryListAll();
                permsList = new ArrayList<>(menuList.size());
                for (SysMenu menu : menuList) {
                    permsList.add(menu.getPerms());
                }
            }else {
                //普通用户授权
                permsList = sysUserMapper.queryAllPerms(userId);
            }
    
            Set<String> permsSet = new HashSet<>();
            for (String perms : permsList) {
                if (StringUtils.isBlank(perms)) {
                    continue;
                }
                permsSet.addAll(Arrays.asList(perms.trim().split(",")));
            }
            return permsSet;
        }
    }
    

    3.2 mapper的sql语句

      <select id="queryByUserName" resultType="com.qfedu.rongzaiboot.entity.SysUser">
        select * from sys_user where username = #{username}
      </select>
    
        <select id="queryAllPerms" resultType="java.lang.String">
          SELECT perms FROM sys_menu m
            LEFT JOIN sys_role_menu rm ON m.menu_id = rm.menu_id
            LEFT JOIN sys_user_role ur ON ur.role_id = rm.role_id
            WHERE ur.user_id = #{userId}
        </select>
    
      <select id="queryListAll" resultType="com.qfedu.rongzaiboot.entity.SysMenu">
        select menu_id,name,url,perms,type,icon,order_num,(select p.name from sys_menu p where p.menu_id = m.parent_id) as parentName
        from sys_menu m order by m.order_num asc
      </select>
    

    4. LoginController中添加登录controller方法

    import com.google.code.kaptcha.Constants;
    import com.google.code.kaptcha.Producer;
    import com.qfedu.rongzaiboot.utils.R;
    import com.qfedu.rongzaiboot.utils.ShiroUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.crypto.hash.Md5Hash;
    import org.apache.shiro.subject.Subject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.imageio.ImageIO;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletResponse;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.util.Map;
    
    @Controller
    public class SysLoginController {
    
        @Autowired
        private Producer producer;
    
        /**
         * 生成验证码
         * @param response
         * @throws IOException
         */
        @RequestMapping("/captcha.jpg")
        public void kaptcha(HttpServletResponse response) throws IOException {
            //避免浏览器缓存
            response.setHeader("Cache-Control", "no-store,no-cache");
            response.setContentType("image/jpeg");
    
            //生成文字验证码
            String text = producer.createText();
    
            //生成图片验证码
            BufferedImage image = producer.createImage(text);
    
            //保存在session中
            ShiroUtils.setSessionAttribute(Constants.KAPTCHA_SESSION_KEY, text);
    
            //响应给客户端
            ServletOutputStream outputStream = response.getOutputStream();
            ImageIO.write(image, "jpg", outputStream);
            outputStream.flush();//清空缓冲区
        }
    
        /**
         * 登陆方法
         * @param map
         * @return
         */
        @ResponseBody
        @RequestMapping("/sys/login")
        public R login(@RequestBody Map<String,String> map) {
            String username = map.get("username");
            String password = map.get("password");
            String captcha = map.get("captcha");
            String rememberMe = map.get("rememberMe");
    
            String sessionCaptcha = ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY);
            if (!captcha.equalsIgnoreCase(sessionCaptcha)) {
                return R.error("验证码不正确");
            }
    
            boolean remember = false;
            if (rememberMe != null){
                remember = true;
            }
    
            Subject subject = ShiroUtils.getSubject();
    
            try {
                password = new Md5Hash(password,username,1024).toHex();
                UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    
                //设置记住我
                token.setRememberMe(remember);
    
                subject.login(token);
            }catch (UnknownAccountException e){
                return R.error(e.getMessage());
            }catch (IncorrectCredentialsException e){
                return R.error(e.getMessage());
            }catch (LockedAccountException e){
                return R.error(e.getMessage());
            }catch (AuthenticationException e){
                return R.error("账户验证失败");
            }
    
            return R.ok();
        }
    
        /**
         * 退出方法
         * @return
         */
        @GetMapping("/logout")
        public String logout(){
            ShiroUtils.logout();
            return "redirect:login.html";
        }
    
    }
    

    5. shiro缓存的配置 文件ehcache.xml

    <ehcache>
        <diskStore path="java.io.tmpdir"/>
        <defaultCache
    
                maxEntriesLocalHeap="2000"
                maxElementsInMemory="10000"
                eternal="false"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                overflowToDisk="true" />
    </ehcache>
    
    Ehcache配置文件的详细说明
    [http://blog.csdn.net/mlitsn/article/details/1909192](http://blog.csdn.net/mlitsn/article/details/1909192)
    
     缓存存活时间和失效时间:
    [http://www.cnblogs.com/sprinkle/p/6539086.html](http://www.cnblogs.com/sprinkle/p/6539086.html) 
    
    设置缓存的大小
    [http://elim.iteye.com/blog/2116749](http://elim.iteye.com/blog/2116749)
    
    

    相关文章

      网友评论

        本文标题:springboot整合shiro

        本文链接:https://www.haomeiwen.com/subject/kbtvbftx.html