springboot集成shiro实现用户权限认证和sh

shiro是apache下的一个开源的权限认证框架。相对于spring security来说是一个轻量级的安全认证组件。今天用shiro讲原来的权限认证给替换掉。总结一下如下:

第一步肯定是要引入shiro的相关依赖的:

<!-- shiro -->
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring</artifactId>
   <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring-boot-starter</artifactId>
	<version>1.4.0-RC2</version>
</dependency>

第二步就是編寫shiro相關的。从用户登录开始吧:

**
     * 登陆操作
     * @param request
     * @return
     */
    @ApiOperation(value="登陆操作", notes="描述")
    @RequestMapping(value = "login.do", method = RequestMethod.POST)
    @ResponseBody
    public ResultMsg loginDo(HttpServletRequest request){
        //验证码校验
        if (!CaptchaUtils.checkVerifyCode(request)) {
            return ResultMsg.fail("验证码有误!");
        }
        String name = request.getParameter("name");
        String password = request.getParameter("password");
        //用户名密码校验
        UsernamePasswordToken token = new UsernamePasswordToken(name, MD5Util.MD5(password));
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
        } catch (Exception e) {
            e.printStackTrace();
            return ResultMsg.fail("用户名或密码错误!");
        }
        return ResultMsg.ok();
    }

注意点:

UsernamePasswordToken token = new UsernamePasswordToken(name, MD5Util.MD5(password));
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);

这三行代码是关键。见该用户名和密码封装shiro内置touken对象。获取主题。提交登录信息给shiro认证。看源码就会

发现最后调用的是:info = this.doGetAuthenticationInfo(token);方法

 public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        AuthenticationInfo info = this.getCachedAuthenticationInfo(token);
        if (info == null) {
            info = this.doGetAuthenticationInfo(token);
            log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
            if (token != null && info != null) {
                this.cacheAuthenticationInfoIfPossible(token, info);
            }
        } else {
            log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
        }

        if (info != null) {
            this.assertCredentialsMatch(token, info);
        } else {
            log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
        }

        return info;
    }
那么我们就集成这个抽象类public abstract class AuthenticatingRealm extends CachingRealm implements Initializable {重写认证方法如下:
package com.nuanshui.mintleaf.common;

import com.nuanshui.mintleaf.modules.core.dao.CoreButtonDao;
import com.nuanshui.mintleaf.modules.core.dao.CoreMenuDao;
import com.nuanshui.mintleaf.modules.core.dao.CoreRoleDao;
import com.nuanshui.mintleaf.modules.core.entity.CoreButton;
import com.nuanshui.mintleaf.modules.core.entity.CoreMenu;
import com.nuanshui.mintleaf.modules.core.entity.CoreRole;
import com.nuanshui.mintleaf.modules.core.dao.CoreUserDao;
import com.nuanshui.mintleaf.modules.core.entity.CoreUser;
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.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

@Slf4j
public class Realm extends AuthorizingRealm{

    @Autowired
    private CoreUserDao coreUserDao;

    @Autowired
    private CoreRoleDao coreRoleDao;

    @Autowired
    private CoreMenuDao coreMenuDao;

    @Autowired
    private CoreButtonDao coreButtonDao;


    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //获取用户名
        String userName = (String)SecurityUtils.getSubject().getPrincipal();
        //根据用户名查询用户对象
        CoreUser coreUser = coreUserDao.getCoreUserByname(userName);
        //根据用户获取用户的角色
        List<CoreRole> userRoles = coreRoleDao.getUserRolesById(coreUser.getId());
        userRoles.forEach(coreRole -> {
            log.info("角色的名称:{}",coreRole.getName());
            info.addRole(coreRole.getName());
            //根据角色获取用户菜单
            List<CoreMenu> menus = coreMenuDao.findMenuByRole(coreRole.getName());
            //根据角色获取按钮信息
            List<CoreButton> buttons = coreButtonDao.findButtonByRole(coreRole.getName());
            menus.forEach(coreMenu -> {
                log.info("菜单权限:{}",coreMenu.getPermission());
                info.addStringPermission(coreMenu.getPermission());
            });
            buttons.forEach(coreButton -> {
                log.info("按钮权限:{}",coreButton.getPermission());
                info.addStringPermission(coreButton.getPermission());
            });
        });
        return info;
    }


    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //获得当前用户的用户名
        String username = (String)authenticationToken.getPrincipal();
        //从数据库中根据用户名查找用户
        CoreUser coreUser = coreUserDao.getCoreUserByname(username);
        if(coreUser==null){
            new UnknownAccountException("账号不存在!");
        }
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(coreUser.getName(), coreUser.getPsw(),getName());
        return info;
    }
}
doGetAuthenticationInfo方法中验证用户信息。实现登录成功。那么第一步登录认证就完成了

接下来就是授权。授权调用doGetAuthorizationInfo()方法根据用户信息后去用户的权限。然后交给shiro去处理。shiro值负责去验证是否有权限。而权限的赋值需要开发去实现。

/**
 * 进入新增页面
 * @return
 */
@RequiresPermissions("auth:user:add")
@ApiOperation(value="进入新增页面", notes="描述")
@RequestMapping(value="add.html",method = {RequestMethod.GET})
public ModelAndView add(){
    ModelAndView view =new ModelAndView("modules/core/coreuser/add.html");
    return view;
}

@RequiresPermissions("auth:user:add")这一行注解表示该方法需要授权认证是否有权限。去掉则不认证。不认真就不会去调用doGetAuthorizationInfo()方法。

然后就是配置shiroconfig.这是一个配置类。配置类的作用就是告诉shiro那些路径需要拦截认证授权。那些不需要拦截直接匿名访问。如下:

package org.mintleaf.config;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.mintleaf.common.MyExceptionResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.mintleaf.common.Realm;
import org.springframework.web.servlet.HandlerExceptionResolver;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Author: MengchuZhang
 * @Date: 2018/8/13 16:29
 * @Version 1.0
 */
@Configuration
public class ShiroConfig {

    //定义路径拦截的规则
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //拦截器.
        Map<String, String> filterMap = new LinkedHashMap<>();
        // 配置不会被拦截的链接 顺序判断

        filterMap.put("/css/**", "anon");
        filterMap.put("/images/**", "anon");
        filterMap.put("/script/**", "anon");
        filterMap.put("/plugins/**", "anon");
        filterMap.put("/login.do", "anon");
        filterMap.put("/captcha/getCaptcha.jpg", "anon");
        //文件上传
        filterMap.put("/upload/**", "anon");
        //视频播放
        filterMap.put("/stshipinb/player.html", "anon");
        filterMap.put("/stshipinb/ckplayer.html", "anon");
        filterMap.put("/stshipinb/hlsplayer.html", "anon");

        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterMap.put("/loginOut.do", "logout");

        //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterMap.put("/**", "authc");
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login.html");


        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index.html");

        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403.html");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        return shiroFilterFactoryBean;
    }


    //出事化securityManager安全管理器并且注入realm.如果还有其他操作。注入ream要放到最后
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(myRealm());
        return defaultWebSecurityManager;
    }

    //出事话ream对象
    @Bean
    public Realm myRealm() {
        Realm myRealm = new Realm();
        return myRealm;
    }

    //配置统一异常处理
    @Bean
    public HandlerExceptionResolver solver(){
        HandlerExceptionResolver handlerExceptionResolver=new MyExceptionResolver();
        return handlerExceptionResolver;
    }

}

接下来就是统一异常处理。如果用户没有权限访问。那么我们就给它跳转到无权限访问页面。

package org.mintleaf.common;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

/** 
* @Description: 统一异常处理
* @Param:  
* @return:  
* @Author: liyingying
* @Date:  
*/ 
public class MyExceptionResolver implements HandlerExceptionResolver{

    public ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("==============异常开始=============");
        //如果是shiro无权操作,因为shiro 在操作auno等一部分不进行转发至无权限url
        if(ex instanceof UnauthorizedException){
            ModelAndView mv = new ModelAndView("/403.html");
            return mv;
        }
        ex.printStackTrace();
        ModelAndView mv = new ModelAndView("/403.html");
        mv.addObject("exception", ex.toString().replaceAll("\n", "<br/>"));
        return mv;
    }



}

这样就实现了一套比较完整的shiro权限认证

猜你喜欢

转载自blog.csdn.net/liyingying111111/article/details/88582079