springboot整合shiro

1. pom.xml文件引入依赖


    1.3.2




    org.apache.shiro
    shiro-spring
    ${shiro.version}



    org.apache.shiro
    shiro-ehcache
    ${shiro.version}

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 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 userPermissions = sysUserService.getUserPermissions(user.getUserId());

        SimpleAuthorizationInfo authorInfo = new SimpleAuthorizationInfo();
        authorInfo.addStringPermissions(userPermissions);

        //角色授权
        /*List roleList = Arrays.asList("admin");
        authorInfo.addRoles(roleList);*/

        //资源授权
        /*List 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 getUserPermissions(Long userId) {
        List permsList = null;

        //超级管理员
        if (userId == 1) {
            List menuList = sysMenuMapper.queryListAll();
            permsList = new ArrayList<>(menuList.size());
            for (SysMenu menu : menuList) {
                permsList.add(menu.getPerms());
            }
        }else {
            //普通用户授权
            permsList = sysUserMapper.queryAllPerms(userId);
        }

        Set 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语句

  

    

  

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 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配置文件的详细说明
[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)