shiro登录、权限步骤

shiro 登录

/**
	 * 登录
	 * @param userName
	 * @param password
	 * @return
	 */
	@RequestMapping(value = "/login",method = RequestMethod.POST)
	public Result login(String userName,  String password) {
		System.out.println("---------------------------------------"+userName+userName);
		Result result = new Result();
		Subject user = SecurityUtils.getSubject();
		//获取随机成为的唯一认证
		Session session = user.getSession();
		//登陆校验
		UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
		try {
			user.login(token);
			//自定义的对象(主要用于存储想要的登录信息)
			ShiroAccount shiroAccount = (ShiroAccount) user.getPrincipal();
			if (user.isAuthenticated()) { // 验证身份通过
			//常量,用于存储需要的数据
				session.setAttribute("userLogin", user.getPrincipal());
			}
			// 给用户jwt加密生成token
			// String jwtToken = JwtUtil.sign(shiroAccount, 60L* 1000L* 30L);
			String jwtToken = session.getId().toString();
			result.setData(shiroAccount);
			result.setSuccess(true);
			result.setToken(jwtToken);
			result.setMsg("登录成功");
		} catch (UnknownAccountException e) {
			// throw new RuntimeException("账号不存在!", e);
			result.setSuccess(false);
			result.setMsg("账号不存在!");
		} catch (DisabledAccountException e) {
			// throw new RuntimeException("账号未启用!", e);
			result.setSuccess(false);
			result.setMsg("账号未启用!");
		} catch (IncorrectCredentialsException e) {
			// throw new RuntimeException("密码错误!", e);
			result.setSuccess(false);
			result.setMsg("密码错误!");
		} catch (Throwable e) {
			// throw new RuntimeException(e.getMessage(), e);
			result.setSuccess(false);
			result.setMsg(e.getMessage());
		}
		return result;
	}

     /**
	 * 退出登录
	 * @param userName
	 * @param password
	 * @return
	 */
@RequestMapping(method = RequestMethod.POST, value="logout")
	public Result logout(HttpSession session) {
		Result result =new Result();
		Subject subject = SecurityUtils.getSubject();
		subject.logout();
		result.setData("");
		result.setSuccess(true);
		return result;
	}

退出和登录主要涉及到的是:Shiro的Subject对象详解:
登录的认证步骤:

package com.fcar.kj.manager.shiro;

import com.fcar.kj.common.entities.ShiroAccount;
import com.fcar.kj.common.entities.account.SysAccount;
import com.fcar.kj.common.entities.account.SysRole;
import com.fcar.kj.manager.bsj.service.RoleService;
import com.fcar.kj.manager.service.AccountService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.util.ByteSource;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;


/**
 * shiroRealm配置
 *
 * @date 2020/7/16 20:35
 */
@Slf4j
public class UserRealm extends AuthorizingRealm {
    @Autowired
    private AccountService accountService;
    @Autowired
    private RedisSessionDAO redisSessionDAO;
    @Autowired
    private RoleService roleService;

    public UserRealm() {
        super();
    }

    /**
     * 授权(权限)
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        ShiroAccount shiroAccount = (ShiroAccount) principals.getPrimaryPrincipal();
        Integer id = shiroAccount.getId();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        // TODO 加个批量方法
//        Set roles = accountService.findRoles(id);
//        Set permissions = accountService.findPermissions(id);
        Set<String> roleSet = accountService.findRolesSetByUserId(id);
        if (roleSet.isEmpty()) {
            authorizationInfo.setRoles(roleSet);
            authorizationInfo.setStringPermissions(new HashSet<>());
            return authorizationInfo;
        }
        Set<String> permissions = accountService.findPermissionSetByUserId(id);
        authorizationInfo.setRoles(roleSet);
        authorizationInfo.setStringPermissions(permissions);
        return authorizationInfo;

    }


    /**
     * 认证(登录认证)
     *
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        Object password = token.getCredentials();

        if (username == null || username.length() == 0 || password == null) {
            throw new IncorrectCredentialsException();//用户名、密码为空
        }

        SysAccount account = accountService.findByUsername(username);
        if (account == null) {
            throw new UnknownAccountException();//没找到帐号
        }

        if (account.getLocked() == 1) {
            throw new LockedAccountException(); //帐号锁定
        }
        Integer userId = account.getUserid();
        //保存登录的参数
        ShiroAccount shiroUser = new ShiroAccount(userId, account.getUsername());
        shiroUser.setOrganizationid(account.getOrganizationid());
        // TODO 根据用户id查询角色信息
        List<SysRole> sysRoleList = accountService.findRoleListByUserId(userId);
        if (sysRoleList.isEmpty()){
            throw new LockedAccountException(); //帐号锁定
        }
        shiroUser.setSysRoleList(sysRoleList);

        //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现。参数:用户名、密码、salt=username+salt、realm name
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(shiroUser, account.getPassword(),
                ByteSource.Util.bytes(account.getCredentialsSalt()), getName());
        return authenticationInfo;
    }

    /**
     * 清除认证信息(修改密码)
     *
     * @param userIds
     */
    public void clearCachedAuthenticationInfoByUserId(List<Integer> userIds) {
        if (null == userIds || userIds.size() == 0) {
            return;
        }
        List<SimplePrincipalCollection> list = getSpcListByUserIds(userIds);
        RealmSecurityManager securityManager =
                (RealmSecurityManager) SecurityUtils.getSecurityManager();
        UserRealm realm = (UserRealm) securityManager.getRealms().iterator().next();
        for (SimplePrincipalCollection simplePrincipalCollection : list) {
            realm.clearCachedAuthenticationInfo(simplePrincipalCollection);
        }
    }

    /**
     * 根据userId 清除当前session存在的用户的权限缓存(修改角色权限)
     *
     * @param userIdList 已经修改了权限的userId
     */
    public void clearAuthorizationByUserId(List<Integer> userIdList) {
        if (null == userIdList || userIdList.size() == 0) {
            return;
        }
        List<SimplePrincipalCollection> list = getSpcListByUserIds(userIdList);
        RealmSecurityManager securityManager =
                (RealmSecurityManager) SecurityUtils.getSecurityManager();
        UserRealm realm = (UserRealm) securityManager.getRealms().iterator().next();
        for (SimplePrincipalCollection simplePrincipalCollection : list) {
            realm.clearCachedAuthorizationInfo(simplePrincipalCollection);
        }
    }

    /**
     * 根据用户id获取所有spc
     *
     * @param userIdList 已经修改了权限的userId
     */
    private List<SimplePrincipalCollection> getSpcListByUserIds(List<Integer> userIdList) {
        //获取所有session
        Collection<Session> sessions = redisSessionDAO.getActiveSessions();
        //定义返回
        List<SimplePrincipalCollection> list = new ArrayList<>();
        for (Session session : sessions) {
            //获取session登录信息。
            Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            if (null != obj && obj instanceof SimplePrincipalCollection) {
                //强转
                SimplePrincipalCollection spc = (SimplePrincipalCollection) obj;
                //判断用户,匹配用户ID。
                obj = spc.getPrimaryPrincipal();
                if (null != obj && obj instanceof ShiroAccount) {
                    ShiroAccount user = (ShiroAccount) obj;
                    log.info("重置的user:" + user);
                    //比较用户ID,符合即加入集合
                    if (null != user && userIdList.contains(user.getId())) {
                        list.add(spc);
                    }
                }
            }
        }
        return list;
    }
}

登录校验:

@Slf4j
public class MySessionManager extends DefaultWebSessionManager {
	private static final String AUTHORIZATION = "Authorization";
	 
    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
 
    public MySessionManager() {
        super();
    }
 
    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
         String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
        if(StringUtils.isEmpty(id)){
           id = request.getParameter(AUTHORIZATION);
        }
        //如果请求头中有 Authorization 则其值为sessionId
        if (!StringUtils.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
            //否则按默认规则从cookie取sessionId 可以防止XSS攻击
            Serializable sessionId = super.getSessionId(request, response);
            log.info("sessionId={}",sessionId);
            return sessionId;
        }
    }
}

获取登录信息:

package com.fcar.kj.manager.utils;

import com.fcar.kj.bsj.common.constant.CoreConstant;
import com.fcar.kj.common.entities.ShiroAccount;
import org.apache.shiro.SecurityUtils;

/**
 * 获取用户id
 *
 * @author 李晨亮
 * @date 2020-07-09 15:51
 **/
public class UserUtil {

    /**
     * 获取登录用户id
     *
     * @return
     */
    public static int getUserId() {
        ShiroAccount shiroAccount = getShiroAccount();
        int userId = shiroAccount.getId();
        return userId;
    }

    /**
     * 查询用户数据权限
     *
     * @return
     */
    public static int getDataScopeType() {
        ShiroAccount shiroAccount = getShiroAccount();
        Integer dataScopeType = shiroAccount.getSysRoleList().get(0).getDataScopeType();
        return dataScopeType == null ? 0 : dataScopeType;
    }

    /**
     * 获取机构id
     *
     * @return
     */
    public static int getOrgId() {
        ShiroAccount shiroAccount = getShiroAccount();
        return shiroAccount.getOrganizationid();
    }

    /**
     * 获取登录用户信息
     *
     * @return
     */
    public static ShiroAccount getShiroAccount() {
        return (ShiroAccount) SecurityUtils.getSubject().getSession().getAttribute("userLogin");
    }
}

初始化Shiro的缓存机制:

package com.fcar.kj.manager.config;

import com.auth0.jwt.internal.org.apache.commons.lang3.StringUtils;
import com.fcar.kj.bsj.common.constant.CoreConstant;
import com.fcar.kj.bsj.common.utils.StringUtil;
import com.fcar.kj.common.entities.account.SysResource;
import com.fcar.kj.manager.dao.bsj.SysResourceDao;
import com.fcar.kj.manager.shiro.MySessionManager;
import com.fcar.kj.manager.shiro.UserRealm;
import com.fcar.kj.manager.shiro.filter.CustomFormAuthenticationFilter;
import com.fcar.kj.manager.shiro.filter.CustomPermissionsAuthorizationFilter;
import com.fcar.kj.manager.utils.MyExceptionHandler;
import com.fcar.kj.manager.utils.WebHashedCredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;

import javax.annotation.Resource;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Configuration
public class ShiroConfig {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    //    @Value("${spring.redis.timeout}")
//    private int timeout;
    @Value("${spring.redis.password}")
    private String password;

    @Resource
    private SysResourceDao sysResourceDao;

//这里是初时话Shiro拦截的过滤机制
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 设置自定义的过滤器, 配置过滤器,跨域访问时,对OPTIONS的请求返回true,这个很重要,不然不能获取header上的值
        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
        filters.put("authc", new CustomFormAuthenticationFilter());
        filters.put("perms", new CustomPermissionsAuthorizationFilter());

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //注意过滤器配置顺序 不能颠倒
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了,登出后跳转配置的loginUrl
        //anon: 无需认证即可访问
        //authc: 需要认证才可访问
		//user: 点击“记住我”功能可访问
		//perms: 拥有权限才可以访问
		//role: 拥有某个角色权限才能访问
        filterChainDefinitionMap.put("/user/logout", "logout");
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/user/ajaxLogin", "anon");
        filterChainDefinitionMap.put("/user/login", "anon");
        //pdf授权访问路径对外打开
        filterChainDefinitionMap.put("/fession/ossauth", "anon");
        // 上传图片和视频
        filterChainDefinitionMap.put("/ueditor/getConfig", "anon");
        filterChainDefinitionMap.put("/article/getUploadImgUrl", "anon");
        filterChainDefinitionMap.put("/article/getUploadAdviceUrl", "anon");
        // 查询数据库权限配置
        List<SysResource> resourceList = sysResourceDao.selectAllResource(CoreConstant.AVAILABLE_NORMAL);
        for (SysResource sysResource : resourceList) {
            String path = sysResource.getPath();
            String permission = sysResource.getPermission();
            if (StringUtils.isNotBlank(path) && StringUtils.isNotBlank(permission)) {
                String perm = "perms[" + permission + "]";
                filterChainDefinitionMap.put(path, perm);
            }
        }

        filterChainDefinitionMap.put("/**", "authc");
        //配置shiro默认登录界面地址,前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据
        shiroFilterFactoryBean.setLoginUrl("/user/login");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 凭证匹配器
     * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     * )
     *
     * @return
     */
    @Bean("name = webHashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
//        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
//        hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5(""));
//        return hashedCredentialsMatcher;
        return new WebHashedCredentialsMatcher();
    }


//已下就是初始化UserRealm登录存储额缓存机制(这里使用redis作为缓存工具)
    @Bean
    public UserRealm myShiroRealm() {
        UserRealm myShiroRealm = new UserRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }


    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager());
        return securityManager;
    }

    //自定义sessionManager
    @Bean
    public SessionManager sessionManager() {
        MySessionManager mySessionManager = new MySessionManager();
        mySessionManager.setSessionDAO(redisSessionDAO());
        mySessionManager.setGlobalSessionTimeout(43200000);//12小时
        return mySessionManager;
    }

    /**
     * 配置shiro redisManager
     * 

* 使用的是shiro-redis开源插件 * * @return */ public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost(host + ":" + port); // redisManager.setExpire(1800);// 配置缓存过期时间 // redisManager.setTimeout(timeout); redisManager.setPassword(password); return redisManager; } /** * cacheManager 缓存 redis实现 *

* 使用的是shiro-redis开源插件 * * @return */ @Bean public RedisCacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); return redisCacheManager; } /** * RedisSessionDAO shiro sessionDao层的实现 通过redis *

* 使用的是shiro-redis开源插件 */ @Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); return redisSessionDAO; } /** * 开启shiro aop注解支持. * 使用代理方式;所以需要开启代码支持; * * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * 注册全局异常处理 * * @return */ @Bean(name = "exceptionHandler") public HandlerExceptionResolver handlerExceptionResolver() { return new MyExceptionHandler(); } }

这里是拦截器的过滤机制顺便解决了跨域问题(这里没有解决到权限访问的问题):

package com.fcar.kj.manager.shiro.filter;

import com.alibaba.fastjson.JSONObject;
import com.fcar.kj.common.utils.Result;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * 自定义权限过滤器
 *
 * @date 2020-07-24 15:05
 **/
public class CustomPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {

    /**
     * 屏蔽OPTIONS请求
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return
     * @throws IOException
     */
    @Override
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
        // 判断请求是否是options请求
        String method = WebUtils.toHttp(request).getMethod();
        if (StringUtils.equalsIgnoreCase("OPTIONS", method)) {
            return true;
        }
        return super.isAccessAllowed(request, response, mappedValue);
    }

    /**
     * 解决权限不足302问题
     *
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        Subject subject = getSubject(request, response);
        if (subject.getPrincipal() == null) {
            saveRequestAndRedirectToLogin(request, response);
        } else {
            WebUtils.toHttp(response).setContentType("application/json; charset=utf-8");
            WebUtils.toHttp(response).getWriter().print(JSONObject.toJSONString(new Result<>().fali("无权限")));
        }
        return false;
    }

}

package com.fcar.kj.manager.shiro.filter;

import com.alibaba.fastjson.JSONObject;
import com.fcar.kj.common.utils.Result;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * 自定义认证过滤器
 *
 * @date 2020/7/24 15:54
 */
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {

    /**
     * 屏蔽OPTIONS请求
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        boolean accessAllowed = super.isAccessAllowed(request, response, mappedValue);
        if (!accessAllowed) {
            // 判断请求是否是options请求
            String method = WebUtils.toHttp(request).getMethod();
            if (StringUtils.equalsIgnoreCase("OPTIONS", method)) {
                return true;
            }
        }
        return super.isAccessAllowed(request, response, mappedValue);
    }

    /**
     * 解决未登录302问题
     *
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (isLoginRequest(request, response)) {
            if (isLoginSubmission(request, response)) {
                return executeLogin(request, response);
            } else {
                return true;
            }
        } else {
            // 返回固定的JSON串
            WebUtils.toHttp(response).setContentType("application/json; charset=utf-8");
            WebUtils.toHttp(response).getWriter().print(JSONObject.toJSONString(new Result<>().fali("未登录")));
            return false;
        }
    }

}

自定义角色权限过滤:

import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * @Date: 2019/4/18 0018 17:40
 * @Description:支持多角色验证
 * 由于Shiro filterChainDefinitions中 roles默认是and,
 * = user,roles[system,general]
 * 比如:roles[system,general] ,表示同时需要“system”和“general” 2个角色才通过认证
 * 但是在实际业务中,一个端口的权限往往对多个角色开放(比如管理员可以使用一切权利)
 */
public class MyRolesAuthorizationFilter extends AuthorizationFilter{

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
            throws Exception {
        Subject subject = getSubject(request, response);
        String[] rolesArray = (String[]) mappedValue;
        //没有权限访问
        if (rolesArray == null || rolesArray.length == 0) {
            return true;
        }
        for (int i = 0; i < rolesArray.length; i++) {
            //若当前用户是rolesArray中的任何一个,则有权限访问
            if (subject.hasRole(rolesArray[i])) {
                return true;
            }
        }
        return false;
    }
}

在ShiroConfig引用,其实spring boot集成shiro也就这两步,加上自定义权限验证 和自定义登录验证。

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;

@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean shirFilter(DefaultWebSecurityManager securityManager) {
        //System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //添加自定义验证方法,注意这个Filter继承javax.servlet.Filter
        Map<String, Filter> filterMap=new HashMap<>();
        filterMap.put("rolesOr",roleFilter());
        shiroFilterFactoryBean.setFilters(filterMap);
        //拦截器.
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/gif/**", "anon");
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/sanyi/logout", "logout");
        //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
        //
        filterChainDefinitionMap.put("/sanyi/index", "anon");/*swagger*/
        filterChainDefinitionMap.put("/swagger-ui.html", "anon");
        filterChainDefinitionMap.put("/swagger-resources/**", "anon");
        filterChainDefinitionMap.put("/v2/**", "anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");
        /*business业务端(1管理员,3业务员)*/
        filterChainDefinitionMap.put("/contract/base/**", "authc,rolesOr[1,3]");
     /*其他,一定要采取白名单*/
        filterChainDefinitionMap.put("/**", "authc");
        // 如果不设置默认会自动寻找Web工程根目录下的"/login"页面
        shiroFilterFactoryBean.setLoginUrl("/sanyi/index");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/");
        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/sanyi/nopermission");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public MyRolesAuthorizationFilter roleFilter(){
        MyRolesAuthorizationFilter roleFilter=new MyRolesAuthorizationFilter();
        return roleFilter;
    }

    /**
     * 密码凭证匹配器
     * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     * )
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(10);//散列的次数10哈哈;
        return hashedCredentialsMatcher;
    }

    /**
     * 身份认证realm; (这个需要自己写,账号密码校验;权限等)
     * @return
     */
    @Bean
    public MyShiroRealm myShiroRealm(){
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    /**
     * 会话管理器
     * @return
     */
    @Bean
    public DefaultWebSessionManager sessionManager(){
        DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(1800000);//单位是ms
        return sessionManager;
    }

    @Bean
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     *  开启shiro aop注解支持.
     *  使用代理方式;所以需要开启代码支持;
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean(name="simpleMappingExceptionResolver")
    public SimpleMappingExceptionResolver
    createSimpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
        Properties mappings = new Properties();
        mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理
        mappings.setProperty("UnauthorizedException","403");
        r.setExceptionMappings(mappings);  // None by default
        r.setDefaultErrorView("error");    // No default
        r.setExceptionAttribute("exception");     // Default is "exception"
        //r.setWarnLogCategory("example.MvcLogger");     // No default
        return r;
    }

}

你可能感兴趣的:(shiro登录、权限步骤)