精华内容
下载资源
问答
  • shiro权限控制

    2020-03-04 16:05:57
    shiro权限控制 书接上文(如有逻辑问题请看上文转载内容) 配置类 ShiroConfig import com.youotech.login.dao.UserDao; import com.youotech.login.entity.Permission; import org.apache.shiro.spring.security....

    shiro权限控制

    书接上文(如有逻辑问题请看上文转载内容)

    配置类

    ShiroConfig

    import com.youotech.login.dao.UserDao;
    import com.youotech.login.entity.Permission;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.apache.shiro.mgt.SecurityManager;

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

    /**

    • Created by EalenXie on 2019/3/25 15:12.
      */
      @Configuration
      public class ShiroConfig {

      @Resource
      private UserDao permissionRepository;

      @Resource
      private UserRealm userRealm;

      /**

      • 配置 资源访问策略 . web应用程序 shiro核心过滤器配置
        */
        @Bean
        public ShiroFilterFactoryBean factoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);
        //加上放开option配置
        Map<String, Filter> filters = factoryBean.getFilters();
        // 注意这里不要用Bean的方式,否则会报错
        filters.put(“authc”, new ShiroUserFilter());
        factoryBean.setFilters(filters);
        //控制登录
        factoryBean.setLoginUrl("/login");//登录页
        factoryBean.setSuccessUrl("/index");//首页
        factoryBean.setUnauthorizedUrl("/unauthorized");//未授权界面;
        factoryBean.setFilterChainDefinitionMap(setFilterChainDefinitionMap()); //配置 拦截过滤器链
        return factoryBean;
        }

      /**

      • 配置 SecurityManager,可配置一个或多个realm
        */
        @Bean
        public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        // securityManager.setRealm(xxxxRealm);
        return securityManager;
        }

      /**

      • 开启shiro 注解支持. 使以下注解能够生效 :
      • 需要认证 {@link org.apache.shiro.authz.annotation.RequiresAuthentication RequiresAuthentication}
      • 需要用户 {@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser}
      • 需要访客 {@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest}
      • 需要角色 {@link org.apache.shiro.authz.annotation.RequiresRoles RequiresRoles}
      • 需要权限 {@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions}
        */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
        }

      /**

      • 配置 拦截过滤器链. map的键 : 资源地址 ; map的值 : 所有默认Shiro过滤器实例名
      • 默认Shiro过滤器实例 参考 : {@link org.apache.shiro.web.filter.mgt.DefaultFilter}
        */
        private Map<String, String> setFilterChainDefinitionMap() {
        Map<String, String> filterMap = new LinkedHashMap<>();
        //注册 数据库中所有的权限 及其对应url
        List allPermission = permissionRepository.selectPrmission();//数据库中查询所有权限
        for (Permission p : allPermission) {
        filterMap.put(p.getUrl(), “perms[” + p.getName() + “]”); //拦截器中注册所有的权限
        }
        filterMap.put("/static/", “anon”); //公开访问的资源
        filterMap.put("/sys/
        ", “anon”); //公开接口地址
        filterMap.put("/logout", “logout”); //配置登出页,shiro已经帮我们实现了跳转
        filterMap.put("/**", “authc”); //所有资源都需要经过验证
        return filterMap;
        }
        }

    ShiroSessionUtils

    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;

    public class ShiroSessionUtils {

    public static void setAttribute(Object key, Object value) {
    	Subject account = SecurityUtils.getSubject();
    	if (null != account) {
    		Session session = account.getSession();
    		if (null != session) {
    			session.setAttribute(key, value);
    			session.setTimeout(28800000);//8个小时
    		}
    	}
    }
    
    public static Object getAttribute(Object key) {
    	Subject account = SecurityUtils.getSubject();
    	if (null != account) {
    		Session session = account.getSession();
    		if (null != session) {
    			return session.getAttribute(key);
    		}
    	}
    	return null;
    }
    
    public static Object removeAttribute(Object key) {
    	Subject account = SecurityUtils.getSubject();
    	if (null != account) {
    		Session session = account.getSession();
    		if (null != session) {
    			return session.removeAttribute(key);
    		}
    	}
    	return null;
    }
    

    }

    ShiroUserFilter

    import org.apache.shiro.web.filter.authc.UserFilter;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.RequestMethod;

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

    /**

    • 重写shiro的UserFilter,实现通过OPTIONS请求

    • @author MDY
      */
      public class ShiroUserFilter extends UserFilter {

      /**

      • 在访问过来的时候检测是否为OPTIONS请求,如果是就直接返回true
        */
        @Override
        protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
        setHeader(httpRequest,httpResponse);
        return true;
        }

        return super.preHandle(request,response);
        }

      /**

      • 该方法会在验证失败后调用,这里由于是前后端分离,后台不控制页面跳转
      • 因此重写改成传输JSON数据
        */
        @Override
        protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
        saveRequest(request);
        setHeader((HttpServletRequest) request,(HttpServletResponse) response);
        PrintWriter out = response.getWriter();
        //自己控制返回的json数据
        //out.println(new Result(ResultStatusCode.SHIRO_ERROR));
        out.flush();
        out.close();
        }

      /**

      • 为response设置header,实现跨域
        */
        private void setHeader(HttpServletRequest request, HttpServletResponse response){
        //跨域的header设置
        response.setHeader(“Access-control-Allow-Origin”, request.getHeader(“Origin”));
        response.setHeader(“Access-Control-Allow-Methods”, request.getMethod());
        response.setHeader(“Access-Control-Allow-Credentials”, “true”);
        response.setHeader(“Access-Control-Allow-Headers”, request.getHeader(“Access-Control-Request-Headers”));
        //防止乱码,适用于传输JSON数据
        response.setHeader(“Content-Type”,“application/json;charset=UTF-8”);
        response.setStatus(HttpStatus.OK.value());
        }
        }

    ShiroUtils

    import com.youotech.login.entity.SysUserEntity;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.crypto.hash.SimpleHash;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;

    /**

    • Shiro工具类
      */
      public class ShiroUtils {

      /** 加密算法 /
      public final static String hashAlgorithmName = “SHA-256”;
      /
      * 循环次数 */
      public final static int hashIterations = 16;

      public static String sha256(String password, String salt) {
      return new SimpleHash(hashAlgorithmName, password, salt, hashIterations).toString();
      }

      public static Session getSession() {
      return SecurityUtils.getSubject().getSession();
      }

      public static Subject getSubject() {
      return SecurityUtils.getSubject();
      }

      public static SysUserEntity getUserEntity() {
      return (SysUserEntity)SecurityUtils.getSubject().getPrincipal();
      }

      public static Integer getUserId() {
      return getUserEntity().getId();
      }

      public static void setSessionAttribute(Object key, Object value) {
      getSession().setAttribute(key, value);
      }

      public static Object getSessionAttribute(Object key) {
      return getSession().getAttribute(key);
      }

      public static boolean isLogin() {
      return SecurityUtils.getSubject().getPrincipal() != null;
      }

      public static void logout() {
      SecurityUtils.getSubject().logout();
      }
      }

    UserRealm

    import com.youotech.login.dao.UserDao;
    import com.youotech.login.entity.Permission;
    import com.youotech.login.entity.RoleModel;
    import com.youotech.login.entity.SysUserEntity;
    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.stereotype.Component;

    import javax.annotation.Resource;
    import java.util.List;

    @Component
    public class UserRealm extends AuthorizingRealm {

    @Resource
    private UserDao userRepository;
    
    /**
     * 权限核心配置 根据数据库中的该用户 角色 和 权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        SysUserEntity user = (SysUserEntity) principals.getPrimaryPrincipal();
        //根据用户id获取角色列表
        List<RoleModel> roles = userRepository.selectRoleByUserId(user.getId());
        if(roles!=null && roles.size()>0){
            for (RoleModel role : roles) {
                authorizationInfo.addRole(role.getName());
                //根据角色id获取权限列表
                List<Permission> permissions=userRepository.selectPrmissionById(role.getId());
                if(permissions!=null && permissions.size()>0){
                    for (Permission permission : permissions) {
                        authorizationInfo.addStringPermission(permission.getName());
                    }
                }
            }
        }
        return authorizationInfo;
    }
    
    /**
     * 用户登陆 凭证信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken)authcToken;
    
        //查询用户信息
        SysUserEntity user = new SysUserEntity();
        user.setLoginName(token.getUsername());
        user = userRepository.selectByName(user.getLoginName());
    
        //把user放到session
        if(user!=null){
            ShiroSessionUtils.setAttribute("loginUser",user);
        }
    
        //账号不存在
        if(user == null) {
            throw new UnknownAccountException("账号或密码不正确");
        }
    
    
        //账号锁定
        if(user.getState() == 0){
            throw new LockedAccountException("账号已被锁定,请联系管理员");
        }
    
        System.out.println(getName()+"qqqqqqqqqqqq");
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPwd(),getName());
        //清除之前的授权信息
        super.clearCachedAuthorizationInfo(info.getPrincipals());
        return info;
     }
    }
    

    登录模块login

    controller

    import com.youotech.common.Result;
    import com.youotech.common.VerifyCode;
    import com.youotech.login.entity.SysUserEntity;
    import com.youotech.login.service.LoginService;
    import com.youotech.shiro.ShiroSessionUtils;
    import com.youotech.shiro.ShiroUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.subject.Subject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.context.request.RequestAttributes;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;

    import javax.imageio.ImageIO;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.List;

    /**

    • 登录相关

    /
    @Controller
    @CrossOrigin(allowCredentials = “true”, allowedHeaders = "
    ")
    public class SysLoginController {
    @Autowired
    private LoginService loginService;

    /**
     * 登录
     */
    @ResponseBody
    @RequestMapping(value = "/sys/login", method = RequestMethod.GET)
    public Result login(String loginName, String pwd,String verifyCode) {
    	try {
    		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
    		if (requestAttributes == null) {
    			return new Result(false,500,"requestAttributes==null");
    		}
    		HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
    		if (request == null) {
    			return new Result(false,500,"requestAttributes==null");
    		}
    		Object code = request.getSession().getAttribute("verifyCode");
    		if (code == null) {
    			return new Result(false,500,"验证码已过期");
    		}
    		if (!code.toString().toLowerCase().equals(verifyCode)) {
    			return new Result(false,500,"验证码错误");
    		}
    		Subject subject = ShiroUtils.getSubject();
    		UsernamePasswordToken token = new UsernamePasswordToken(loginName,
    				pwd);
    		subject.login(token);
    
    	} catch (UnknownAccountException e) {
    		return new Result(false, 500, e.getMessage());
    	}  catch (LockedAccountException e) {
    		return new Result(false, 500, "账号已被锁定,请联系管理员");
    	}
    	SysUserEntity user  = null;
    	try {
    		user = (SysUserEntity) ShiroSessionUtils.getAttribute("loginUser");
    	} catch (Exception e) {
    		e.printStackTrace();
    	}
    	System.out.println("SysUserEntity===>"+user.getLoginName()+"=="+user.getPwd());
    	return new Result(true,200,"success");
    }
    
    /**
     * 退出
     */
    @RequestMapping(value = "/sys/logout", method = RequestMethod.GET)
    public String logout() {
    	ShiroUtils.logout();
    	return "login";
    }
    
    /**
     * 获取当前登录用户信息
     */
    @RequestMapping(value = "/sys/getLoginUserInfo", method = RequestMethod.GET)
    @ResponseBody
    public SysUserEntity getLoginUserInfo() {
    	try {
    		SysUserEntity user = ShiroUtils.getUserEntity();
    		return user;
    	} catch (Exception e) {
    	}
    	return null;
    }
    
    
    /**
     * 获取菜单栏
     *
     * @return
     */
    @RequestMapping("getMenuData")
    @ResponseBody
    public Result getMenuData() {
    	List<String> urlList = loginService.getData();
    	return new Result(false,200,"返回权限信息成功",urlList);
    }
    
    /**
     * 获取验证码
     * @param response
     * @param request
     */
    @ResponseBody
    @RequestMapping(value = "/sys/getVerifyCode", method = RequestMethod.GET)
    @CrossOrigin(allowCredentials = "true")
    public void getVerificationCode(HttpServletResponse response, HttpServletRequest request) {
    
    	try {
    
    		int width = 300;
    
    		int height = 69;
    
    		BufferedImage verifyImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    

    //生成对应宽高的初始图片

    		String randomText = VerifyCode.drawRandomText(width, height, verifyImg);
    

    //单独的一个类方法,出于代码复用考虑,进行了封装。

    //功能是生成验证码字符并加上噪点,干扰线,返回值为验证码字符

    		request.getSession().setAttribute("verifyCode", randomText);
    
    		response.setContentType("image/png");//必须设置响应内容类型为图片,否则前台不识别
    
    		OutputStream os = response.getOutputStream(); //获取文件输出流
    
    		ImageIO.write(verifyImg, "png", os);//输出图片流
    
    		os.flush();
    
    		os.close();//关闭流
    
    	} catch (IOException e) {
    
    		e.printStackTrace();
    
    	}
     }
    }
    

    dao

    import com.youotech.login.entity.Permission;
    import com.youotech.login.entity.RoleModel;
    import com.youotech.login.entity.SysUserEntity;
    import org.apache.ibatis.annotations.Mapper;

    import java.util.List;

    @Mapper
    public interface UserDao {
    /**
    * [新增]
    * @author itdaoyang
    * @date 2020/03/03
    **/
    int insert(SysUserEntity sysUserEntity);

    /**
     * [刪除]
     * @author itdaoyang
     * @date 2020/03/03
     **/
    int delete(int id);
    
    /**
     * [更新]
     * @author itdaoyang
     * @date 2020/03/03
     **/
    int update(SysUserEntity sysUserEntity);
    
    /**
     * [查询] 根据主键 id 查询
     * @author itdaoyang
     * @date 2020/03/03
     **/
    SysUserEntity selectByName(String loginNmae);
    
    /**
     * [查询] 分页查询
     * @author itdaoyang
     * @date 2020/03/03
     **/
    List<SysUserEntity> pageList(int offset, int pagesize);
    
    /**
     * [查询] 分页查询 count
     * @author itdaoyang
     * @date 2020/03/03
     **/
    int pageListCount(int offset,int pagesize);
    
    List<RoleModel> selectRoleByUserId(Integer id);
    
    List<Permission> selectPrmissionById(Integer roleId);
    
    List<Permission> selectPrmission();}
    

    entity

    权限

    import lombok.Data;

    import javax.persistence.Column;
    import java.io.Serializable;
    import java.util.Date;

    /**

    • permission

    • @author 大狼狗 2020-03-03
      */
      @Data
      public class Permission implements Serializable {

      private static final long serialVersionUID = 1L;

      /**

      • id
        */
        private Integer id;

      /**

      • url
        */
        @Column(unique = true)
        private String url;

      /**

      • 名称
        */
        @Column(unique = true)
        private String name;

      /**

      • 创建人
        */
        private String createUser;

      /**

      • 创建时间
        */
        private Date createDate;

    }

    角色

    import java.io.Serializable;
    import lombok.Data;

    import javax.persistence.Column;
    import javax.persistence.FetchType;
    import javax.persistence.ManyToMany;
    import java.util.Date;
    import java.util.List;

    /**

    • role_model

    • @author 大狼狗 2020-03-03
      */
      @Data
      public class RoleModel implements Serializable {

      private static final long serialVersionUID = 1L;

      /**

      • 主键id
        */
        private Integer id;

      /**

      • 类型
        */
        private Integer type;

      /**

      • 名称
        */
        @Column(unique = true)
        private String name;

      /**

      • 36
        */
        private String createUser;

      /**

      • 创建时间
        */
        private Date createDate;

      @ManyToMany(fetch= FetchType.EAGER)
      private List permissions;
      }

    用户

    import lombok.Data;

    import javax.persistence.Column;
    import javax.persistence.FetchType;
    import javax.persistence.ManyToMany;
    import java.io.Serializable;
    import java.util.Date;
    import java.util.List;

    /**

    • sys_user_entity

    • @author itdaoyang 2020-03-02
      */
      @Data
      public class SysUserEntity implements Serializable {

      private static final long serialVersionUID = 1L;

      /**

      • 主键id
        */
        private Integer id;

      /**

      • 真实姓名
        */
        private String userTrueName;

      /**

      • 登录名
        */
        @Column(unique = true)
        private String loginName;

      /**

      • 登录密码
        */
        private String pwd;

      /**

      • 状态(0:正常,1锁定)
        */
        private Integer state;

      /**

      • 错误次数
        */
        private Integer errorCount;

      /**

      • 创建人
        */
        private String createUser;

      /**

      • 创建时间
        */
        private Date createDate;

      /**

      • 最后登录时间
        */
        private Date astLoginTime;

      @ManyToMany(fetch= FetchType.EAGER)
      private List roleModels;

    }

    xml

    <?xml version="1.0" encoding="UTF-8"?>
    <resultMap id="BaseResultMap" type="com.youotech.login.entity.SysUserEntity">
        <result column="id" property="id"/>
        <result column="user_true_name" property="userTrueName"/>
        <result column="login_name" property="loginName"/>
        <result column="pwd" property="pwd"/>
        <result column="state" property="state"/>
        <result column="error_count" property="errorCount"/>
        <result column="create_user" property="createUser"/>
        <result column="create_date" property="createDate"/>
        <result column="ast_login_time" property="astLoginTime"/>
    </resultMap>
    
    <sql id="Base_Column_List">
        id,
        user_true_name,
        login_name,
        pwd,
        state,
        error_count,
        create_user,
        create_date,
        ast_login_time
    </sql>
    
    <insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id"
            parameterType="com.youotech.login.entity.SysUserEntity">
        INSERT INTO u_user
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test='null != userTrueName'>
                user_true_name,
            </if>
            <if test='null != loginName'>
                login_name,
            </if>
            <if test='null != pwd'>
                pwd,
            </if>
            <if test='null != state'>
                state,
            </if>
            <if test='null != errorCount'>
                error_count,
            </if>
            <if test='null != createUser'>
                create_user,
            </if>
            <if test='null != createDate'>
                create_date,
            </if>
            <if test='null != astLoginTime'>
                ast_login_time
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test='null != userTrueName'>
                #{userTrueName},
            </if>
            <if test='null != loginName'>
                #{loginName},
            </if>
            <if test='null != pwd'>
                #{pwd},
            </if>
            <if test='null != state'>
                #{state},
            </if>
            <if test='null != errorCount'>
                #{errorCount},
            </if>
            <if test='null != createUser'>
                #{createUser},
            </if>
            <if test='null != createDate'>
                #{createDate},
            </if>
            <if test='null != astLoginTime'>
                #{astLoginTime}
            </if>
        </trim>
    </insert>
    
    <delete id="delete">
        DELETE FROM u_user
        WHERE id = #{id}
    </delete>
    
    <update id="update" parameterType="com.youotech.login.entity.SysUserEntity">
        UPDATE u_user
        <set>
            <if test='null != userTrueName'>user_true_name = #{userTrueName},</if>
            <if test='null != loginName'>login_name = #{loginName},</if>
            <if test='null != pwd'>pwd = #{pwd},</if>
            <if test='null != state'>state = #{state},</if>
            <if test='null != errorCount'>error_count = #{errorCount},</if>
            <if test='null != createUser'>create_user = #{createUser},</if>
            <if test='null != createDate'>create_date = #{createDate},</if>
            <if test='null != astLoginTime'>ast_login_time = #{astLoginTime}</if>
        </set>
        WHERE id = #{id}
    </update>
    
    
    <select id="selectByName" resultType="com.youotech.login.entity.SysUserEntity">
        SELECT
        *
        FROM u_user
        WHERE login_name = #{loginName}
    </select>
    
    <select id="pageList" resultMap="BaseResultMap">
        SELECT
        <include refid="Base_Column_List"/>
        FROM u_user
        LIMIT #{offset}, #{pageSize}
    </select>
    
    <select id="pageListCount" resultType="java.lang.Integer">
        SELECT count(1)
        FROM u_user
    </select>
    
    <select id="selectRoleByUserId" resultType="com.youotech.login.entity.RoleModel">
      SELECT r.*  FROM u_user_role ur ,u_user u,u_role r WHERE  u.id=ur.uid and r.id=ur.rid and u.id=#{id}
    </select>
    
    <select id="selectPrmissionById" resultType="com.youotech.login.entity.Permission">
       SELECT p.*  FROM u_permission p ,u_role_permission rp,u_role r WHERE  p.id=rp.pid and r.id=rp.rid and r.id=#{roleId}
    </select>
    <select id="selectPrmission" resultType="com.youotech.login.entity.Permission">
        SELECT * from u_permission
    </select>
    

    为什么没有service因为根本用不到

    关于注册的注解使用

    1.访问/sys/getVerifyCode获取验证码
    2.登录
    3.调接口
    4.不通是不是?不通就对了,看controller层上面注解

    @RequiresRoles(“administrator”):控制角色,有此角色的用户才能访问该接口
    @RequiresPermissions(“add”):控制权限,有此权限的用户才能访问该接口

    还有一些感觉用不到,如有需要可以访问上篇转载的文章

    差点忘了pom依赖

    org.apache.shiro shiro-spring 1.2.3

    此外我们还可以通过拦截器控制url拦截

    自定义拦截器PermissionInterceptor

    import com.youotech.login.service.LoginService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.List;

    /**

    • 自定以接口控制拦截器
      */
      @Component
      public class PermissionInterceptor implements HandlerInterceptor {

      @Autowired
      LoginService loginService;

      @Override
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
      try {
      StringBuffer requestURL = request.getRequestURL();
      String url = requestURL.toString();
      //打印路径信息,作为后台日志进行查看
      System.out.println(url);
      if (url.contains("/sys/login")) {
      return HandlerInterceptor.super.preHandle(request, response, handler);
      }
      if (url.contains("/sys/logout")) {
      return HandlerInterceptor.super.preHandle(request, response, handler);
      }
      if (url.contains("/sys/getVerifyCode")) {
      return HandlerInterceptor.super.preHandle(request, response, handler);
      }
      if (url.contains("/sys/getLoginUserInfo")) {
      return HandlerInterceptor.super.preHandle(request, response, handler);
      }
      //判断权限信息
      List urlList = loginService.getData();
      if (urlList != null && urlList.size() > 0) {
      for (String qxurl : urlList) {
      if (url.contains(qxurl)) {
      return HandlerInterceptor.super.preHandle(request, response, handler);
      } else {
      response.setStatus(401);
      return false;
      }
      }
      } else {
      response.setStatus(401);
      return false;
      }
      return false;
      } catch (Exception e) {
      response.setStatus(401);
      return false;
      }
      }
      }

    注册拦截器

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

    @Configuration
    public class MyConfig implements WebMvcConfigurer {

    @Bean
    public PermissionInterceptor myInterceptor(){
        return new PermissionInterceptor();
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 使拦截器生效1.此处参数是我们自定义的拦截器名( LoginInterceptor ) 2.添加拦截规则(/**)拦截全部
        registry.addInterceptor(myInterceptor()).addPathPatterns("/**");
        WebMvcConfigurer.super.addInterceptors(registry);
    }
    

    }

    以上权限基本配完但是存在一个问题,那就是如果session过期怎么办,难道让前端一直卡着??

    所以在ShiroUserFilter中继续配置

    import com.youotech.login.entity.SysUserEntity;
    import org.apache.shiro.web.filter.authc.UserFilter;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.RequestMethod;

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

    /**

    • 重写shiro的UserFilter,实现通过OPTIONS请求

    • @author MDY
      */
      public class ShiroUserFilter extends UserFilter {

      /**

      • 在访问过来的时候检测是否为OPTIONS请求,如果是就直接返回true
        */
        @Override
        protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        //从session中获取用户信息
        SysUserEntity comUser = (SysUserEntity)ShiroSessionUtils.getAttribute(“loginUser”);
        // session过期
        if (comUser == null) {
        returnJson(httpResponse, “{“code”:510,“msg”:“session is expired!”}”);
        setHeader(httpRequest, httpResponse);
        return false;
        }
        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
        setHeader(httpRequest, httpResponse);
        return true;
        }
        return super.preHandle(httpRequest, response);
        }

      /返回客户端数据/
      private void returnJson(HttpServletResponse response, String json) throws Exception{
      PrintWriter writer = null;
      response.setCharacterEncoding(“UTF-8”);
      response.setContentType(“text/html; charset=utf-8”);
      try {
      writer = response.getWriter();
      writer.print(json);

       } catch (IOException e) {
       } finally {
           if (writer != null)
               writer.close();
       }
      

      }

      /**

      • 该方法会在验证失败后调用,这里由于是前后端分离,后台不控制页面跳转
      • 因此重写改成传输JSON数据
        */
        @Override
        protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
        saveRequest(request);
        setHeader((HttpServletRequest) request, (HttpServletResponse) response);
        PrintWriter out = response.getWriter();
        //自己控制返回的json数据
        //out.println(new Result(ResultStatusCode.SHIRO_ERROR));
        out.flush();
        out.close();
        }

      /**

      • 为response设置header,实现跨域
        */
        private void setHeader(HttpServletRequest request, HttpServletResponse response) {
        //跨域的header设置
        response.setHeader(“Access-control-Allow-Origin”, request.getHeader(“Origin”));
        response.setHeader(“Access-Control-Allow-Methods”, request.getMethod());
        response.setHeader(“Access-Control-Allow-Credentials”, “true”);
        response.setHeader(“Access-Control-Allow-Headers”, request.getHeader(“Access-Control-Request-Headers”));
        //防止乱码,适用于传输JSON数据
        response.setHeader(“Content-Type”, “application/json;charset=UTF-8”);
        response.setStatus(HttpStatus.OK.value());
        }
        }

    但是前端会结束不到相应的response值,打开f12一看跨域,所以在启动类中加上跨域

    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.context.annotation.Bean;
    import org.springframework.scheduling.annotation.EnableScheduling;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    import org.springframework.web.filter.CorsFilter;

    @SpringBootApplication(exclude ={DataSourceAutoConfiguration.class})
    @MapperScan({“com.youotech..dao"})
    @EnableTransactionManagement
    @EnableCaching
    @ServletComponentScan
    @EnableScheduling
    @CrossOrigin(allowCredentials = “true”, allowedHeaders = "
    ”)

    public class CzbdzApplication {

    public static void main(String[] args) {
        SpringApplication.run(CzbdzApplication.class, args);
    }
    
    /**
     * 跨域问题
     * @return
     */
    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
    

    // config.addAllowedOrigin(“http://localhost:9000”);
    config.addAllowedOrigin("");
    config.addAllowedHeader("
    ");
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/**", config); // CORS 配置对所有接口都有效
    FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source) );
    bean.setOrder(0);
    return bean;
    }
    }

    解决角色无权限后端异常问题工具类

    import org.apache.shiro.authz.UnauthorizedException;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;

    import java.util.HashMap;
    import java.util.Map;

    /**

    • 统一异常处理类
    • 捕获程序所有异常,针对不同异常,采取不同的处理方式

    */
    @ControllerAdvice
    public class ExceptionHandleController {
    private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionHandleController.class);

    @ResponseBody
    @ExceptionHandler(UnauthorizedException.class)
    public Map<String,Object> handleShiroException(Exception ex) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("code",512);
        return map;
    }
    

    }

    配置加密解密(采用base对称加密与MD5非对称加密)

    修改!!!shiorUtil内参数:改为MD5加密方式

    /**  加密算法 */
    public final static String hashAlgorithmName = "MD5";
    
    public static String MD5(String password, String salt) {
    	return new SimpleHash(hashAlgorithmName, password, salt, hashIterations).toString();
    }
    

    userRealm类添加

    //MD5加密
        String pw = new String(token.getPassword());
        String password  = Base64Utils.dec(pw);
        String credentials =ShiroUtils.MD5(password,"salt");
    
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
        token.setPassword(credentials.toCharArray());
        //清除之前的授权信息
        super.clearCachedAuthorizationInfo(info.getPrincipals());
        return info;
    

    base64工具类

    mport sun.misc.BASE64Decoder;
    import sun.misc.BASE64Encoder;

    import java.util.Base64;

    /**

    • Created by Liuxd on 2018-11-02.
      */
      public class Base64Utils {

      public static void main(String[] args) throws Exception {
      String str = “123456”;
      base64(str);
      enAndDeCode(str);

      }

      public static String dec(String str){
      byte[] bytes = str.getBytes();
      byte[] decoded = Base64.getDecoder().decode(str);
      String decodeStr = new String(decoded);
      return decodeStr;
      }
      /**

      • Base64

      */
      public static void base64(String str) {
      byte[] bytes = str.getBytes();
      //Base64 加密
      String encoded = Base64.getEncoder().encodeToString(bytes);
      System.out.println(“Base 64 加密后:” + encoded);
      //Base64 解密
      byte[] decoded = Base64.getDecoder().decode(encoded);
      String decodeStr = new String(decoded);
      System.out.println(“Base 64 解密后:” + decodeStr);
      System.out.println();
      }

      /**

      • BASE64加密解密
        */
        public static void enAndDeCode(String str) throws Exception {
        String data = encryptBASE64(str.getBytes());
        System.out.println(“sun.misc.BASE64 加密后:” + data);

        byte[] byteArray = decryptBASE64(data);
        System.out.println(“sun.misc.BASE64 解密后:” + new String(byteArray));
        }

      /**

      • BASE64解密
      • @throws Exception
        */
        public static byte[] decryptBASE64(String key) throws Exception {
        return (new BASE64Decoder()).decodeBuffer(key);
        }

      /**

      • BASE64加密
        */
        public static String encryptBASE64(byte[] key) throws Exception {
        return (new BASE64Encoder()).encodeBuffer(key);
        }
        }

    比较类方法doCredentialsMatch

    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;

    public class CredentialsMatcher extends SimpleCredentialsMatcher {
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
    UsernamePasswordToken utoken=(UsernamePasswordToken) token;
    //获得用户输入的密码:(可以采用加盐(salt)的方式去检验)
    String inPassword = new String(utoken.getPassword());
    //获得数据库中的密码
    String dbPassword=(String) info.getCredentials();
    //进行密码的比对
    return this.equals(inPassword, dbPassword);
    }

    展开全文
  • Shiro权限控制

    2019-01-08 10:08:01
    我们都知道Shiro和secitity都是安全的框架,但是相对于...Shiro权限控制1.png 可以看出Shiro除了基本的认证、授权、会话管理、加密之外还有许多特性。 Shiro架构: 从架构来说,主要包含三个概念,Subject、Sec...

    我们都知道Shiro和secitity都是安全的框架,但是相对于Shiro来说,比较入门简单,所需要的功能基本上都能满足,理解起来也会比较容易。

    Shiro是一个有许多特性的全面的安全框架,下面一幅图进行介绍。

    Shiro权限控制1.png

    可以看出Shiro除了基本的认证、授权、会话管理、加密之外还有许多特性。

    Shiro架构:

    从架构来说,主要包含三个概念,Subject、SecurityManager、Realms(重要),在使用的时候我们都是围绕这三个概念来进行编码和使用


    Shiro权限控制2.png

    Shiro的应用不依赖任何的容器,可以在javaSE下使用也可以在javaEE上使用,下面是一张用户登录的例图:


    Shiro权限控制3.png

    首先理解下面的几个类:

    SecurityManager:

    Shiro的核心是ScurityManager,它是负责安全认证与授权的,Shiro本身已经实现了所有的细节,我们在使用的时候可以完全把它当做黑盒使用。

    SecurityUtils:

    本质上是一个工厂,类似Spring中的ApplicationContext,

    Subject:

    Subject是有点难理解的,有些地方理解为user,其实不然,Subject中文翻译是项目,在下面代码中会表现的很清楚

    Realm:

    在Shiro中,进行的授权和认证就是由它来操作的,我已开始对授权和认证不是很理解,也不是很明白在代码操作的时候命名在认证的时候,可以获取到授权的信息,为什么还要在进行授权的操作,下面对授权和认证做一下解释:所谓的认证,他相当于人的身份证,就是可以证明你身份的证件,在应用中,就是拿着当前登录的用户的名称与数据库中查询,看是当前登录的用户是否在数据库存在,如果存在,好,说明你是你自己。而所谓的授权,就相当于当你购买火车票的时候,如果你买的硬座,你就只能去硬座的车厢,而不能去软卧等其他车厢,这就相当于限制了你的能力,而在应用中也相当于如此,在授权的时候,会从数据库将你的权限信息获取到,交给Shiro,如果此时你要去访问指定的页面的时候,会首先对你的权限进行校验,看你是否有该权限,如果有就可以访问,没有则不可以访问。

    代码讲解:

    准备ehcache的xml配置 ,为什么要设置ehcache,是因为如果不设置,会每次刷新页面的时候都会去访问授权的方法,加上缓存之后,可以有效的制止

    
    <?xml version="1.0" encoding="UTF-8"?>
    <!--add by shanggq 2018/8/31 end-->
    <ehcache name="es">
    
    <span class="hljs-tag">&lt;<span class="hljs-name">diskStore</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"java.io.tmpdir"</span>/&gt;</span>
    
    <span class="hljs-comment">&lt;!--
       name:缓存名称。
       maxElementsInMemory:缓存最大数目
       maxElementsOnDisk:硬盘最大缓存个数。
       eternal:对象是否永久有效,一但设置了,timeout将不起作用。
       overflowToDisk:是否保存到磁盘,当系统当机时
       timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
       timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
       diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
       diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
       diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
       memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
        clearOnFlush:内存数量最大时是否清除。
         memoryStoreEvictionPolicy:
            Ehcache的三种清空策略;
            FIFO,first in first out,这个是大家最熟的,先进先出。
            LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
            LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
    --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">defaultCache</span>
            <span class="hljs-attr">maxElementsInMemory</span>=<span class="hljs-string">"10000"</span>
            <span class="hljs-attr">eternal</span>=<span class="hljs-string">"false"</span>
            <span class="hljs-attr">timeToIdleSeconds</span>=<span class="hljs-string">"120"</span>
            <span class="hljs-attr">timeToLiveSeconds</span>=<span class="hljs-string">"120"</span>
            <span class="hljs-attr">overflowToDisk</span>=<span class="hljs-string">"false"</span>
            <span class="hljs-attr">diskPersistent</span>=<span class="hljs-string">"false"</span>
            <span class="hljs-attr">diskExpiryThreadIntervalSeconds</span>=<span class="hljs-string">"120"</span>
    /&gt;</span>
    
    <span class="hljs-comment">&lt;!-- 登录记录缓存锁定10分钟 --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">cache</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"passwordRetryCache"</span>
           <span class="hljs-attr">maxEntriesLocalHeap</span>=<span class="hljs-string">"2000"</span>
           <span class="hljs-attr">eternal</span>=<span class="hljs-string">"false"</span>
           <span class="hljs-attr">timeToIdleSeconds</span>=<span class="hljs-string">"3600"</span>
           <span class="hljs-attr">timeToLiveSeconds</span>=<span class="hljs-string">"0"</span>
           <span class="hljs-attr">overflowToDisk</span>=<span class="hljs-string">"false"</span>
           <span class="hljs-attr">statistics</span>=<span class="hljs-string">"true"</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">cache</span>&gt;</span>
    

    </ehcache>
    <!-- add by zhangcf 2018/8/31 start -->

    pom.xml

    
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.example<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>springbootshiro<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">packaging</span>&gt;</span>jar<span class="hljs-tag">&lt;/<span class="hljs-name">packaging</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>springbootshiro<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">description</span>&gt;</span>Demo project for Spring Boot<span class="hljs-tag">&lt;/<span class="hljs-name">description</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">parent</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.0.4.RELEASE<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">relativePath</span>/&gt;</span> <span class="hljs-comment">&lt;!-- lookup parent from repository --&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">parent</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="hljs-tag">&lt;/<span class="hljs-name">project.build.sourceEncoding</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">project.reporting.outputEncoding</span>&gt;</span>UTF-8<span class="hljs-tag">&lt;/<span class="hljs-name">project.reporting.outputEncoding</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">java.version</span>&gt;</span>1.8<span class="hljs-tag">&lt;/<span class="hljs-name">java.version</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-comment">&lt;!--&lt;exclusions&gt;--&gt;</span>
                <span class="hljs-comment">&lt;!--&lt;exclusion&gt;--&gt;</span>
                    <span class="hljs-comment">&lt;!--&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;--&gt;</span>
                    <span class="hljs-comment">&lt;!--&lt;artifactId&gt;spring-boot-starter-tomcat&lt;/artifactId&gt;--&gt;</span>
                <span class="hljs-comment">&lt;!--&lt;/exclusion&gt;--&gt;</span>
            <span class="hljs-comment">&lt;!--&lt;/exclusions&gt;--&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    
    
        <span class="hljs-comment">&lt;!--!&amp;#45;&amp;#45;用于编译jsp&amp;ndash;&amp;gt;--&gt;</span>
        <span class="hljs-comment">&lt;!--&amp;lt;!&amp;ndash; https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper &amp;ndash;&amp;gt;--&gt;</span>
        <span class="hljs-comment">&lt;!--&lt;dependency&gt;--&gt;</span>
            <span class="hljs-comment">&lt;!--&lt;groupId&gt;org.apache.tomcat.embed&lt;/groupId&gt;--&gt;</span>
            <span class="hljs-comment">&lt;!--&lt;artifactId&gt;tomcat-embed-jasper&lt;/artifactId&gt;--&gt;</span>
        <span class="hljs-comment">&lt;!--&lt;/dependency&gt;--&gt;</span>
    
        <span class="hljs-comment">&lt;!--&amp;lt;!&amp;ndash;jsp页面使用jstl标签&amp;ndash;&amp;gt;--&gt;</span>
        <span class="hljs-comment">&lt;!--&amp;lt;!&amp;ndash; https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl &amp;ndash;&amp;gt;--&gt;</span>
        <span class="hljs-comment">&lt;!--&lt;dependency&gt;--&gt;</span>
            <span class="hljs-comment">&lt;!--&lt;groupId&gt;javax.servlet.jsp.jstl&lt;/groupId&gt;--&gt;</span>
            <span class="hljs-comment">&lt;!--&lt;artifactId&gt;jstl&lt;/artifactId&gt;--&gt;</span>
        <span class="hljs-comment">&lt;!--&lt;/dependency&gt;--&gt;</span>
        <span class="hljs-comment">&lt;!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-configuration-processor --&gt;</span>
        <span class="hljs-comment">&lt;!-- configuration-processor --&gt;</span>
    
        <span class="hljs-comment">&lt;!--用来读取配置文件--&gt;</span>
        <span class="hljs-comment">&lt;!--&lt;dependency&gt;--&gt;</span>
            <span class="hljs-comment">&lt;!--&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;--&gt;</span>
            <span class="hljs-comment">&lt;!--&lt;artifactId&gt;spring-boot-configuration-processor&lt;/artifactId&gt;--&gt;</span>
            <span class="hljs-comment">&lt;!--&lt;optional&gt;true&lt;/optional&gt;--&gt;</span>
        <span class="hljs-comment">&lt;!--&lt;/dependency&gt;--&gt;</span>
    
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>mysql<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mysql-connector-java<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>runtime<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-comment">&lt;!--jpa 对象持久化,利用该jar包,通过bean直接生成数据库表--&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-data-jpa<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-autoconfigure<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    
        <span class="hljs-comment">&lt;!-- shiro的依赖 --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.shiro<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>shiro-spring<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.3.2<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.shiro<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>shiro-ehcache<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.3.2<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    
        <span class="hljs-comment">&lt;!--&amp;lt;!&amp;ndash; https://mvnrepository.com/artifact/net.sf.ehcache/ehcache &amp;ndash;&amp;gt;--&gt;</span>
        <span class="hljs-comment">&lt;!--&lt;dependency&gt;--&gt;</span>
        <span class="hljs-comment">&lt;!--&lt;groupId&gt;net.sf.ehcache&lt;/groupId&gt;--&gt;</span>
        <span class="hljs-comment">&lt;!--&lt;artifactId&gt;ehcache&lt;/artifactId&gt;--&gt;</span>
        <span class="hljs-comment">&lt;!--&lt;version&gt;2.10.5&lt;/version&gt;--&gt;</span>
        <span class="hljs-comment">&lt;!--&lt;/dependency&gt;--&gt;</span>
    
    
        <span class="hljs-comment">&lt;!--   DRUID是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、PROXOOL等DB池的优点,同时加入了日志监控,
        可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池(据说是目前最好的连接池,不知道速度有没有BoneCP快)。--&gt;</span>
        <span class="hljs-comment">&lt;!-- https://mvnrepository.com/artifact/com.alibaba/druid --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.alibaba<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>druid<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.1.10<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    
        <span class="hljs-comment">&lt;!--热部署--&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-devtools<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">optional</span>&gt;</span>true<span class="hljs-tag">&lt;/<span class="hljs-name">optional</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-comment">&lt;!--spring boot 整合 mybatis 依赖--&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.mybatis.spring.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mybatis-spring-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.3.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    
        <span class="hljs-comment">&lt;!-- json支持 --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>net.sf.json-lib<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>json-lib<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.4<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">classifier</span>&gt;</span>jdk15<span class="hljs-tag">&lt;/<span class="hljs-name">classifier</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- 包含支持UI模版(Velocity,FreeMarker,JasperReports), 邮件服务, 脚本服务(JRuby), 缓存Cache(EHCache),
                    任务计划Scheduling(uartz)。 --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-context-support<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- 单点登录 --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.shiro<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>shiro-cas<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.2.4<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">build</span>&gt;</span>
        <span class="hljs-comment">&lt;!--表示最终的项目名,--&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">finalName</span>&gt;</span>springbootshiro<span class="hljs-tag">&lt;/<span class="hljs-name">finalName</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>
            <span class="hljs-comment">&lt;!--用于告诉maven在打包的时候不需要web.xml,否则会报到不到web.xml de错--&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>maven-war-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.4<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">configuration</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">failOnMissingWebXml</span>&gt;</span>false<span class="hljs-tag">&lt;/<span class="hljs-name">failOnMissingWebXml</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">configuration</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">build</span>&gt;</span>
    

    </project>

    下面对代码的讲解,我是按照用户一开始登录,到最后的顺序来讲解,如果按照从数据操作到前端访问开始讲的话,有些地方会比较难理解

    Controller层的数据 -- 前端页面在访问的时候访问的路径

    
    package com.example.springbootshiro.Controller;
    

    import com.example.springbootshiro.service.ILoginService;
    import com.example.springbootshiro.entity.Role;
    import com.example.springbootshiro.entity.User;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.authz.annotation.RequiresPermissions;
    import org.apache.shiro.authz.annotation.RequiresRoles;
    import org.apache.shiro.subject.Subject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;

    import java.util.Map;

    /**

    • @author shanggq
    • @date 2018/8/31
      */
      // add by shanggq 2018/8/31 start
      @RestController
      public class LoginResource {

    // // 注入业务层
    // @Autowired
    // private ILoginService iLoginService;
    //
    @GetMapping("/login")
    public String login() {
    return “login”;
    }

    //
    // POST登录
    @PostMapping("/login")
    public String login(@RequestBody Map map) {
    // 添加用户认证信息
    Subject subject = SecurityUtils.getSubject();

        UsernamePasswordToken usernamePasswordToken = <span class="hljs-keyword">new</span> UsernamePasswordToken(
                map.get(<span class="hljs-string">"username"</span>).toString(),
                map.get(<span class="hljs-string">"password"</span>).toString()
        );
        subject.login(usernamePasswordToken);
        <span class="hljs-keyword">return</span> <span class="hljs-string">"login"</span>;
    }
    

    //
    @RequestMapping("/index")
    public String index(){
    return “index”;
    }
    //
    //
    @PostMapping("/error")
    public String error(){
    return “error”;
    }
    //
    // @RequestMapping("/addUser")
    // public String addUser(@RequestBody Map<String,Object> map){
    // User user = iLoginService.addUser(map);
    // return “addUser id ok \n”+user;
    // }
    //
    角色初始化
    //
    // @RequestMapping(“addRole”)
    // public String addRole(@RequestBody Map<String ,Object> map ){
    // Role role = iLoginService.addRole(map);
    // return “addRole is ok ! \n” +role;
    // }
    //
    注解的使用,表示访问该方法需要怎样的权限和角色
    //
    //
    // @RequiresRoles(“admin”)
    // @RequiresPermissions(“create”)
    // @RequestMapping("/create")
    // public String create(){
    // return “Create success!”;
    // }
    }
    // add by shanggq 2018/8/31 endn

    上面代码显示,如果当前端按照post的格式进行数据访问login的时候,会请求到login的方法中,接收的参数是一个map,也就是说在页面中进行数据请求的时候,会将登录的用户名和密码或者其他数据进行传递,然后使用@RequetsBody将数据封装到map集合中,然后下面将数据放到了 UsernamePasswordToken(用户名密码认证机制)对象中,然后此时将该对象放到了用 SecurityUtils对象获取到的Subject对象中,SecutityUtils在上面提到过,他相当于Application,本质上是一个工厂类,然后使用该工厂获取到了Subject,在上面的构架图中我们可以看到,Application Code最终是给了Subject,然后Subject却又给了SecutityManager对象,也就是说现在可以理解为页面传递的用户的信息会存储在SecutityManager中。

    Realm的编写(最重要的。进行用户的授权和认证)

    
    package com.example.springbootshiro.shiro;
    

    import com.example.springbootshiro.mapper.UserInfoMapper;
    import com.example.springbootshiro.service.ILoginService;
    import com.example.springbootshiro.entity.Permission;
    import com.example.springbootshiro.entity.Role;
    import com.example.springbootshiro.entity.User;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    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.apache.shiro.util.ByteSource;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;

    import javax.annotation.Resource;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;

    /**

    • @author shanggq

    • @date 2018/8/31
      */
      // add by shanggq 2018/8/31 start
      @Component
      public class MyShiroRealm extends AuthorizingRealm {

      /**

      • 用於数据库的数据的访问,
      • @Resource 按照名称进行数据的注入
      • @Autowired 按照类型进行数据的注入
        */
        @Autowired
        private UserInfoMapper tokentokentoken;

      /**

      • 权限信息(授权)

      • @param principals

      • @return 如果用户正常退出,缓存会自动消除

      • 如果用户非正常退出,缓存也会自动消除

      • 如果修改了用户的权限,而用户没有退出系统,修改的权限无法立即生效–需手动实现,放在service中

      • <p>

      • 如果不做缓存,shiro会有自己的时间的间隔机制,时间为2分钟
        /
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        /

        • 当没有使用缓存的时候,不断的刷新页面的话,这个代码会不断的执行,其实没有
        • 必要每次都要重新设置权限的信息,所以需要在放到缓存中进行管理,当放到环迅中这样的haunted
        • doGetAuthorizationInfo就会只执行一次,缓存在过期之后再次
        • */
          SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
          User user = (User) principals.getPrimaryPrincipal();

        List<Role> list = new ArrayList<>();
        list = tokentokentoken.findRoleListByName(user.getUid());

        user.setRoles(list);

    // 设置角色,权限,
    // info.addRole(“admin”);
    // info.addStringPermission(“query”);

    // 从数据库中设置角色和权限,分别设置到 SimpleAuthorizationInfo 中返回

        <span class="hljs-keyword">for</span> (Role role : user.getRoles()) {
            info.addRole(role.getRolename());
            <span class="hljs-keyword">for</span> (Permission permission : role.getPermissions()) {
                info.addStringPermission(permission.getPermission());
            }
        }
        <span class="hljs-keyword">return</span> info;
    }
    
    <span class="hljs-comment">/**
     * 身份认证
     *
     * <span class="hljs-doctag">@param</span> token
     * <span class="hljs-doctag">@return</span>
     * <span class="hljs-doctag">@throws</span> AuthenticationException
     */</span>
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">protected</span> AuthenticationInfo <span class="hljs-title">doGetAuthenticationInfo</span><span class="hljs-params">(AuthenticationToken token)</span> <span class="hljs-keyword">throws</span> AuthenticationException </span>{
    

    // 获取用户的输入的账号
    String userName = (String) token.getPrincipal();

        User user = tokentokentoken.findByUserName(userName);
    
        <span class="hljs-keyword">if</span> (user == <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
        }
    

    // 加密方式,获取密码在存入的时候,加密的盐
    // 明文。若存在,将此用户存放到登录认证info中,不需要我们自己进行密码的比较,shiro会自动给我们进行比较
    return new SimpleAuthenticationInfo(userName, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName());

    }
    

    }
    // add by shanggq 2018/8/31 end

    Realm中分为身份认证和授权两个方法,一开始我对这两个方法不理解,因为我的思路是你身份认证完成之后说明你可以进行登录,这样不就完成控制了么,为什么还需要权限认证,当我在继续往下面看的时候,发现并不是这样的。数据全部的操作全部交给SecurityManager对象去操作。

    下面配置核心对象 SecurityManager。在这里我们使用的是spring boot 搭建的工程,所以这里也使用java配置,来完成对象注入到spring容器中。

    
    package com.example.springbootshiro.shiro;
    

    import org.apache.shiro.cache.ehcache.EhCacheManager;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    import java.util.HashMap;
    import java.util.LinkedHashMap;

    /**

    • 进行相应的bena的初始化

    • @author shanggq

    • @date 2018/8/31
      */
      // add by shanggq 2018/8/31 start
      @Configuration
      public class ShiroConfiguration {
      //将自己的验证方式加入容器,因为自己的的容器配置了授权和认证的方法
      @Bean
      public MyShiroRealm myShiroRealm() {
      MyShiroRealm myShiroRealm = new MyShiroRealm();
      return myShiroRealm;
      }

      /*
      shiro缓存管理器

      • 需要注入其他的实体类中
      • 1,安全管理器。secuityManager 最核心的管理器,但是配置完成之后基本上不会进行操作
      • */
        @Bean
        public EhCacheManager ehCacheManager() {
        System.out.println(“ShiroConfiguration.getEhCacheManager()”);
        EhCacheManager cacheManager = new EhCacheManager();
        // 指定配置shiro的文件
        cacheManager.setCacheManagerConfigFile(“classpath:ehcache-shiro.xml”);
        return cacheManager;
        }

      // 权限管理,配置主要是Realm的管理认证
      @Bean(name = “securityManager”)
      public SecurityManager securityManager() {
      DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
      // 设置realm
      manager.setRealm(myShiroRealm());
      // 注入缓存
      manager.setCacheManager(ehCacheManager());
      return manager;
      }
      // Filter 工厂,设置对应的过滤条件和跳转条件
      @Bean
      public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
      ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
      // 必须要设置的 SecurityManager
      shiroFilterFactoryBean.setSecurityManager(securityManager);
      // 设置的拦截器
      // HashMap<String, String> hashMap = new HashMap<>();
      LinkedHashMap<String, String> hashMap = new LinkedHashMap<>();

       <span class="hljs-comment">//  配置退出的过滤器,其中的具体的退出代码shiro已经替我们实现了</span>
       hashMap.put(<span class="hljs-string">"/logout"</span>, <span class="hljs-string">"logout"</span>);
      

    // 登录页面需要的权限
    hashMap.put("/login", “anon”);

        <span class="hljs-comment">//对所有的用户进行认证 当所有的认证都通过的时候才可以访问路径,</span>
        hashMap.put(<span class="hljs-string">"/**"</span>, <span class="hljs-string">"authc"</span>);
        <span class="hljs-comment">//登录页</span>
        shiroFilterFactoryBean.setLoginUrl(<span class="hljs-string">"/login"</span>);
        <span class="hljs-comment">//登录成功之后跳转到首页</span>
        shiroFilterFactoryBean.setSuccessUrl(<span class="hljs-string">"/index"</span>);
        <span class="hljs-comment">//错误页面。认证不通过的时候跳转</span>
        shiroFilterFactoryBean.setUnauthorizedUrl(<span class="hljs-string">"/error"</span>);
        shiroFilterFactoryBean.setFilterChainDefinitionMap(hashMap);
        <span class="hljs-keyword">return</span> shiroFilterFactoryBean;
    }
    
    <span class="hljs-comment">//    加入注解的使用,不加入这个注解不生效</span>
    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> AuthorizationAttributeSourceAdvisor <span class="hljs-title">authorizationAttributeSourceAdvisor</span><span class="hljs-params">(SecurityManager securityManager)</span> </span>{
        AuthorizationAttributeSourceAdvisor advisor = <span class="hljs-keyword">new</span> AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        <span class="hljs-keyword">return</span> advisor;
    }
    

    }
    // add by shanggq 2018/8/31 end

    首先创建你继承了 AuthorizingRealm的 对象类,将该对象创建出来使用@Bean放到spring的容器中,然后再创建 EhCacheManager 对象,为什么要创建该对象的呢,我们在权限控制的代码中已经加了说明,是因为现权限在开发中一般很少去发生改变,如果我们每次在刷新页面的时候都要去数据库中获取数据,判断当前用户有没有当前的权限,会加大数据库的访问,所以我们可以将数据放在缓存中,这样在每次刷新页面的时候,不会都去访问该授权的代码,而首先是去缓存中查询数据,当缓存过期之后,才会去访问授权部分的代码。

    然后在创建核心的对象SecutityManager对象,将继承身份认证的对象和缓存的对象都放到SecutityManager对象中,然后返回。然后创建下面的 ShiroFilterFactoryBean 对象,该对象在使用传统的配置文件的时候,也需要配置该类,目的是为了进行数据的拦截。将拦截的数据传递给上面创建的对象,然后完成身份和权限的校验,不知道在这里读者有没有在脑海中出现一条思路,也就是说,用户在将数据提交的时候,Subject会接收到数据,然后将数据会传递给SecutityManager对象,这里又有疑问了,数据是怎么传递给SecutityManager对象的呢,所以我就去查看源码,结果找到了下面的代码


    控制4.png

    也就是说,subject会将前端接收到的数据给SecutityManager,然而Realm返回的数据和 EhCacheManager缓存对象添加到SecutityManager对象中,但是该对象需要被谁给触发呢,所以就到了最后面的拦截器,ShiroFilterFactoryBean,那我们是不是可以理解为,当有请求过来的时候,会触发拦截器,而拦截器回去调用SecutityManager对象,而SecutityManager对象再去分别调用Realm和EhcacheManager对象,分别获取数据,此时用户的身份,权限都已经获取到,就到了下面的权限的设置,我们将全部的权限信息放到了 LinkedHashMap 集合中,而且还可以使用 ShiroFilterFactoryBean 对象设置登录、失败、首页所需要跳转的页面,然后将集合放到 ShiroFilterFactoryBean 对象中。下面对权限的信息进行说明(常用的五种)

      Anon:表示可以不用登录直接访问
    

    Authc:表示需要登录之后才可以访问

    Perms:表示权限

    Roles:表示角色

    User:表示用户

    AuthorizationAttributeSourceAdvisor对象表示的是开启对方法上的注解的扫描,因为有些方法是在当具有一定的权限的时候才可以访问的,如果不初始化该类的实例,方法上的注解是不起作用的,原因如下
    

    是因为,代理的 方式,应该都知道,java的代理方式,一种是传统的代理方式,当有接口的时候才会使用,一种是cglib代理的方式,传统的代理方式是针对接口而言的,由于此时接口上是没有shiro的注解的,所以此时的注解是不起作用的,所以需要对上面的对象进行实例化,当然也可以改用cglib代理的方式,选择代理的方式是如果有接口就是用传统的代理方式,如果没有接口则使用cglib代理的方式。

    文章来源:https://blog.csdn.net/weixin_38297879/article/details/82258119
    推荐阅读:https://www.roncoo.com/course/list.html?courseName=Shiro

          </div>
    
    展开全文
  • Shiro 权限控制

    2017-10-18 16:42:31
    权限认证,也就是访问控制,即在应用中控制谁能访问哪些资源。 在权限认证中,最核心的三个要素是:权限,角色和用户; 权限,即操作资源的权利,比如访问某个页面,以及对某个模块的数据的添加,修改,删除,查看的...

    权限认证核心要素

    权限认证,也就是访问控制,即在应用中控制谁能访问哪些资源。 在权限认证中,最核心的三个要素是:权限,角色和用户; 权限,即操作资源的权利,比如访问某个页面,以及对某个模块的数据的添加,修改,删除,查看的权利; 角色,是权限的集合,一中角色可以包含多种权限; 用户,在 Shiro 中,代表访问系统的用户,即 Subject;

    授权:

    1、编程式授权

    1.1 基于角色的访问控制 1.2 基于权限的访问控制

    2、注解式授权

    @RequiresAuthentication 要求当前 Subject 已经在当前的 session 中被验证通过才能被访问或调用。 @RequiresGuest 要求当前的 Subject 是一个"guest",也就是说,他们必须是在之前的 session 中没有被验证或被记住才 能被访问或调用。 @RequiresPermissions("account:create") 要求当前的 Subject 被允许一个或多个权限,以便执行注解的方法。 @RequiresRoles("administrator") 要求当前的 Subject 拥有所有指定的角色。如果他们没有,则该方法将不会被执行,而 且 AuthorizationException 异常将会被抛出。 @RequiresUser RequiresUser 注解需要当前的 Subject 是一个应用程序用户才能被注解的类/实例/方法访问或调用。一个“应 用程序用户”被定义为一个拥有已知身份,或在当前 session 中由于通过验证被确认,或者在之前 session 中的'RememberMe' 服务被记住。

    3、Jsp 标签授权

    <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

    Guest 标签:用户没有身份验证时显示相应信息,即游客访问信息; User 标签:用户已经身份验证/记住我登录后显示相应的信息; Authenticated 标签:用户已经身份验证通过,即 Subject.login 登录成功,不是记住我登录的。 notAuthenticated 标签:用户没有身份验证通过,即没有调用 Subject.login 进行登录,包括记住我自动登录 的也属于未进行身份验证。 principal 标签 显示用户身份信息,默认调用 Subject.getPrincipal()获取,即 Primary Principal。 hasRole 标签 如果当前 Subject 有角色将显示 body 体内容。 lacksRole 标签 如果当前 Subject 没有角色将显示 body 体内容。 hasAnyRoles 标签 如果当前 Subject 有任意一个角色(或的关系)将显示 body 体内容。 hasPermission 标签 如果当前 Subject 有权限将显示 body 体内容。 lacksPermission 标签 如果当前 Subject 没有权限将显示 body 体内容。

    Permissions 对权限深入理解

    单个权限 query 单个资源多个权限 user:query user:add 多值 user:query,add 单个资源所有权限 user:query,add,update,delete user:* 所有资源某个权限 *:view 实例级别的权限控制 单个实例的单个权限 printer:query:lp7200 printer:print:epsoncolor 所有实例的单个权限 printer:print:* 所有实例的所有权限 printer:*:* 单个实例的所有权限 printer:*:lp7200 单个实例的多个权限 printer:query,print:lp7200

    项目架构

    1.pom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.java.shiro</groupId>
      <artifactId>shiro03</artifactId>
      <version>0.0.1-SNAPSHOT</version>
     
       <dependencies>
          <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.4</version>
        </dependency>
        
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.12</version>
        </dependency>
        
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.37</version>
        </dependency>
        
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        
        
      </dependencies>
    </project>

     

    2.建立对应的配置文件

    建立数据库连接配置文件jdbc_realm.ini

    [main]
    jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
    dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
    dataSource.driverClass=com.mysql.jdbc.Driver
    dataSource.jdbcUrl=jdbc:mysql://localhost:3306/test
    dataSource.user=root
    dataSource.password=root
    jdbcRealm.dataSource=$dataSource
    securityManager.realms=$jdbcRealm

    基于角色的访问控制配置文件shiro_role.ini

    [users]
    java1234=123456,role1,role2
    jack=123,role1

    基于权限的访问控制配置文件 shiro_permission.ini

    [users]
    java1234=123456,role1,role2
    jack=123,role1
    [roles]
    role1=user:select
    role2=user:add,user:update,user:delete

     

    建立log4j.properties

     

    3.建立common 公共类

    package com.java.common;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    
    public class ShiroUtil {
    
        public static Subject login(String configFile,String userName,String password){
            // 读取配置文件,初始化SecurityManager工厂
            Factory<SecurityManager> factory=new IniSecurityManagerFactory(configFile);
            // 获取securityManager实例
            SecurityManager securityManager=factory.getInstance();
            // 把securityManager实例绑定到SecurityUtils
            SecurityUtils.setSecurityManager(securityManager);
            // 得到当前执行的用户
            Subject currentUser=SecurityUtils.getSubject();
            // 创建token令牌,用户名/密码
            UsernamePasswordToken token=new UsernamePasswordToken(userName, password);
            try{
                // 身份认证
                currentUser.login(token);    
                System.out.println("身份认证成功!");
            }catch(AuthenticationException e){
                e.printStackTrace();
                System.out.println("身份认证失败!");
            }
            return currentUser;
        }
    }

     

    4.基于角色的访问控制

    package com.java.shiro;
    
    
    import java.util.Arrays;
    
    import org.apache.shiro.subject.Subject;
    import org.junit.Test;
    
    import com.java.common.ShiroUtil;
    /**
     * 基于角色的访问控制
     * @author Administrator
     *
     */
    public class RoleTest {
    
        @Test
        public void testHasRole() {
            Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "java1234", "123456");
            // Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "jack", "123");
            System.out.println(currentUser.hasRole("role1")?"有role1这个角色":"没有role1这个角色");
            boolean []results=currentUser.hasRoles(Arrays.asList("role1","role2","role3"));
            System.out.println(results[0]?"有role1这个角色":"没有role1这个角色");
            System.out.println(results[1]?"有role2这个角色":"没有role2这个角色");
            System.out.println(results[2]?"有role3这个角色":"没有role3这个角色");
            System.out.println(currentUser.hasAllRoles(Arrays.asList("role1","role2"))?"role1,role2这两个角色都有":"role1,role2这个两个角色不全有");
            
            currentUser.logout();
        }
    
        @Test
        public void testCheckRole() {
            Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "java1234", "123456");
            // Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "jack", "123");
            currentUser.checkRole("role1");
            currentUser.checkRoles(Arrays.asList("role1","role2"));
            currentUser.checkRoles("role1","role2","role3");
            
            currentUser.logout();
        }
    }
    

     

    5.基于权限的访问控制

    package com.java.shiro;
    
    
    import org.apache.shiro.subject.Subject;
    import org.junit.Test;
    
    import com.java.common.ShiroUtil;
    /**
     * 基于权限的访问控制
     * @author Administrator
     *
     */
    public class PermissionTest {
    
        @Test
        public void testIsPermitted() {
            Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "java1234", "123456");
            // Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "jack", "123");
            System.out.println(currentUser.isPermitted("user:select")?"有user:select这个权限":"没有user:select这个权限");
            System.out.println(currentUser.isPermitted("user:update")?"有user:update这个权限":"没有user:update这个权限");
            boolean results[]=currentUser.isPermitted("user:select","user:update","user:delete");
            System.out.println(results[0]?"有user:select这个权限":"没有user:select这个权限");
            System.out.println(results[1]?"有user:update这个权限":"没有user:update这个权限");
            System.out.println(results[2]?"有user:delete这个权限":"没有user:delete这个权限");
            System.out.println(currentUser.isPermittedAll("user:select","user:update")?"有user:select,update这两个权限":"user:select,update这两个权限不全有");
            
            currentUser.logout();
        }
    

    6.测试
        

    @Test
        public void testCheckPermitted() {
            Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "java1234", "123456");
            // Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "jack", "123");
            currentUser.checkPermission("user:select");
            currentUser.checkPermissions("user:select","user:update","user:delete");
            currentUser.logout();
        }
    }
    


     

    展开全文
  • Shiro权限控制+整合shiro

    万次阅读 多人点赞 2019-03-02 13:17:56
    Shiro权限控制 0.1传统的权限认证方式 特点:为每个人单独的分配权限模块,能够实现权限控制,但是当公司人员庞大之后,非常难管理 上述权限控制如何设计表? 关系:员工和菜单权限的关系:多对多 员工id 菜单...

    Shiro权限控制

    0.1传统的权限认证方式

    1538968548578

    特点:为每个人单独的分配权限模块,能够实现权限控制,但是当公司人员庞大之后,非常难管理

    上述权限控制如何设计表?

    关系:员工和菜单权限的关系:多对多

    员工id 菜单名称
    1 取派管理
    2 快递员管理
    2 运单管理

    好处:可以方便的 实现权限控制

    缺陷:比如当修改权限的时候,公司统一的给组长级别的人 加一个“计算工资”权限,这时候,得修改权限表中所有组长的权限,每个组长在数据库中都得增加一条“计算工资”记录的权限

    后来,这个“计算工资”的功能,在给组长之后,发现,这个权限不合适,得收回这个权限,这个时候,需要删除多条记录

    0.2 RBAC认证方式:

    Role Based Access Controller :基于角色的访问控制

    前无古人后无来者

    在这里插入图片描述

    0.3 RBAC认证方式下的数据库设计

    数据库设计:
    在这里插入图片描述

    1. 权限控制

    1.1 概述

    1.1.1 什么是认证和授权

    • 认证和授权,控制项目资源的访问。
      • 认证:先进行认证。例如:是否是QQ会员-----在程序中指的就是:登录
      • 授权:再进行授权。例如:QQ会员级别(级别不同权限不同)-------对菜单的访问控制

    1.1.2 权限控制的解决方案

    • 方式1:自定义实现
      • 自己如何实现一个认证?写一个Filter过滤器
    • 方式2:采用框架,例如:shiro、Spring Security 等
      • Shiro:轻量、简单,apache
      • Spring Security:轻量、简单,Spring

    1.1.3 Shiro概述

    Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

    官网:http://shiro.apache.org/
    在这里插入图片描述

    Authentication:身份认证/登录,验证用户是不是拥有相应的身份;

    Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

    Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;

    Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

    Web Support:Web支持,可以非常容易的集成到Web环境;

    Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;

    Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;

    Testing:提供测试支持;

    Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

    Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

    1.1.4 三个核心组件

    • 三个核心组件:Subject, SecurityManager 和 Realms.
      • Subject,主体,即“当前操作用户”。Subject代表了当前用户的安全操作。(需要被认证的对象)
      • SecurityManager:它是Shiro框架的核心,管理所有用户的安全操作,并提供安全管理的各种服务。
      • Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    1.1.5 传统的登录和shiro登录的比较

    shiro的使用不依赖Spring框架,javase可以用,javaee也可以用,移动应用程序可以用,大型的网络和企业应用程序也可以使用Shiro。

    传统登录方式:

    在这里插入图片描述

    Shiro安全框架实现登录

    传统的方式,客户端发出请求给Controller,Controller接受用户的用户名和密码,然后由Controller调用业务逻辑,在Controller中调用Service,然后service负责调用数据库进行处理,如果用户名和密码正确,则将用户信息保存到session中,并且进入主页面

    如果用户名和密码错误,则保存错误信息,然后将信息输出到客户端。

    在这里插入图片描述

    答:客户端发送请求到Controller,Controller接受用户的用户名和密码,第一步还是一样的,但是第二步不一样了,以前第二步是Controller直接调用Service处理,现在Controller调用Shiro安全框架去处理,也就是说将认证授权抽取出来,有一个框架专门为你做认证做授权,这里有框架去帮我们完成认证和授权,然后告诉你这个用户名和密码是否可用还是不可用,当然此时,认证成功之后,只要从shiro中取出认证的结果,如果成功的话,将用户保存至session中,然后在跳转页面

    这个过程相比于早期的操作,相当于验证用户名和密码的业务逻辑交给shiro安全框架来做。并且加密也交给shiro安全框架来做,然后由shiro安全框架来加密,由shiro拿密文与数据库中的密文进行比较。

    总结一下:shiro就是一个安全框架,帮助我们解决认证、授权、加密和密码比较的过程。

    1.2 整合shiro

    1.2.1 maven坐标

    步骤1:在common-parent项目中,添加坐标

    <!--shiro start-->
        <dependency>
          <groupId>org.apache.shiro</groupId>
          <artifactId>shiro-ehcache</artifactId>
          <version>1.3.2</version>
        </dependency>
       <dependency>
    		<groupId>org.apache.shiro</groupId>
    		<artifactId>shiro-spring</artifactId>
    		<version>1.3.2</version>
    	</dependency>
    	<!--shiro end-->
    

    1.2.2 配置过滤器

    使用Shiro时,需要配置的相关权限过滤器如下(共10个):

    在这里插入图片描述

    anon: 匿名过滤器,未登陆也可以访问

    authc: 认证过滤器, 登陆后访问

    perms : 需要xx权限,才能访问

    roles: 需要xx角色,才能访问

    user: 需要xx用户,才能访问

    port:指定端口才能访问

    ssl:必须使用https协议才能访问

    logout :登出功能

    rest :根据指定HTTP请求访问才能访问 ,get方式提交 或者 post方式提交才能访问

    1.2.3 配置config类

    shiro的配置步骤 
    1 配置安全管理器SecurityManager
    
    2 realm域配置:由于SecurityManger需要使用realm域,涉及到用户信息、权限信息,处理用户信息的时候需要加密 
    
    3 密码比较器:用户输入的铭文进行加密,并且与数据库中的密文进行比较 
    
    4 配置生成过滤器的工厂类
    
    

    在这里插入图片描述

    /**
     * 在ShiroConfig中做什么事情呢?
     * 1 配置shiro安全管理器,向安全管理器中注入Realm域
     * 2 配置Realm域:注入密码比较器
     * 3 配置密码比较器
     * 4 配置拦截路径和放行路径
     */
    @Configuration
    public class ShiroConfig {
        /**
         * 配置安全管理器,并且注入Realm域
         * @param realm
         * @return
         */
        @Bean
        public SecurityManager securityManager(Realm realm){
            DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
            securityManager.setRealm(realm);
            return securityManager;
        }
    
        /**
         *  Credentials:凭证/证书 ---
         *
         * 配置Realm域,注入密码比较器
         * @param credentialsMatcher
         * @return
         */
        @Bean
        public BosRealm realm(CredentialsMatcher credentialsMatcher){
            BosRealm bosRealm = new BosRealm();
            bosRealm.setCredentialsMatcher(credentialsMatcher);
            return bosRealm;
        }
    
        /**
         * 密码比较器
         *
         * @return
         */
        @Bean
        public CredentialsMatcher credentialsMatcher(){
    //    return new HashedCredentialsMatcher("MD5");
            return new BosCredentialsMatcher();
        }
    
        /**
         * 配置拦截路径和放行路径
         * @param securityManager
         * @return
         */
        @Bean
        public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
            System.out.println("ShiroConfiguration.shirFilter()");
            // shiro过滤器工厂类
            ShiroFilterFactoryBean shiroFilterFactoryBean  = new ShiroFilterFactoryBean();
    
            // 必须设置 SecurityManager
            shiroFilterFactoryBean.setSecurityManager(securityManager);
    
            //拦截器----Map集合
            Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
    
            //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
            filterChainDefinitionMap.put("/login*", "anon");
            filterChainDefinitionMap.put("/user/login*", "anon");
            filterChainDefinitionMap.put("/validatecode.jsp*", "anon");
            filterChainDefinitionMap.put("/css/**", "anon");
            filterChainDefinitionMap.put("/js/**", "anon");
            filterChainDefinitionMap.put("/images/**", "anon");
            filterChainDefinitionMap.put("/data/**", "anon");
    
            //   /** 匹配所有的路径
            //  通过Map集合组成了一个拦截器链 ,自顶向下过滤,一旦匹配,则不再执行下面的过滤
            //  如果下面的定义与上面冲突,那按照了谁先定义谁说了算
            //  /** 一定要配置在最后
            filterChainDefinitionMap.put("/**", "authc");
    
            // 将拦截器链设置到shiro中
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    
    
            // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
            shiroFilterFactoryBean.setLoginUrl("/login.html");
            // 登录成功后要跳转的链接
            shiroFilterFactoryBean.setSuccessUrl("/index.html");
            //未授权界面;
            shiroFilterFactoryBean.setUnauthorizedUrl("/403");
    
    
            return shiroFilterFactoryBean;
        }
    
        /**
         * 开启shiro aop注解支持
         * 使用代理方式;所以需要开启代码支持
         * @param securityManager
         * @return
         */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
            return authorizationAttributeSourceAdvisor;
        }
    
    
        /**
         * 开启cglib代理
         * @return
         */
        @Bean
        public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
            DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
            creator.setProxyTargetClass(true);
            return creator;
        }
    
    }
    

    根据配置文件报错显示,需要创建如下文件:

    1 Realm域

    2 CredentialsMatcher密码比较器

    1.2.4 创建类Realm类

    在这里插入图片描述

    //自定义Realm ,实现安全数据 连接
    public class BosRealm extends AuthorizingRealm {
    
        // 认证...
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) {
            System.out.println("shiro 认证管理... ");
            return null;
        }
    
        @Override
        // 授权...
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
            System.out.println("shiro 授权管理...");
            return null;
        }
    }
    

    1.2.5 编写密码比较器

    在这里插入图片描述

    public class BosCredentialsMatcher extends SimpleCredentialsMatcher {
    
        @Override
        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
            System.out.println("密码比较器");
            return false;
        }
    }
    

    1.2.6 实现认证方法

    在这里插入图片描述

    //自定义Realm ,实现安全数据 连接
    public class BosRealm extends AuthorizingRealm {
        
    
        @Autowired
        private UserService userService;
    
        // 认证...
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) {
            System.out.println("shiro 认证管理... ");
            //1 用户信息
            UsernamePasswordToken upToken = (UsernamePasswordToken) token;
    
            //2 通过username从数据库中查找 User对象,如果找到,没找到.
            User user = userService.findUserByUsername(upToken.getUsername());
            if(user == null){
                //返回null表示账号不存在
                return null;
            }
    
            return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
        }
    
        @Override
        // 授权...
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
            System.out.println("shiro 授权管理...");
    
            return null;
        }
    }
    

    1.2.7编写加密工具类

    在这里插入图片描述

    
    public class Encrypt {
    	/*
    	 * 散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,
    	 * 常见的散列算法如MD5、SHA等。一般进行散列时最好提供一个salt(盐),比如加密密码“admin”,
    	 * 产生的散列值是“21232f297a57a5a743894a0e4a801fc3”,
    	 * 可以到一些md5解密网站很容易的通过散列值得到密码“admin”,
    	 * 即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,
    	 * 如用户名和ID(即盐);这样散列的对象是“密码+用户名+ID”,这样生成的散列值相对来说更难破解。
    	 */
    	
    	//高强度加密算法,不可逆
    	public static String md5(String password, String salt){
    		return new Md5Hash(password,salt,2).toString();
    	}
    	
    	public static void main(String[] args) {
    		/**
    		 * new Md5Hash("123456","lisi",1) :1b539b60601b934441308049a9526e7d
    		 * new Md5Hash("123456","lisi",2) :42bd4e7685cb11d3ba02716c313cb04b
    		 * new Md5Hash("123456","lisi",3) :16f807d62105b4896034552ee5caeb8a
    		 * new Md5Hash("123456","KMNO4",3):8bd35dc14dc07f756478bb44513694f6
    		 */
    		//System.out.println(new Md5Hash("123456","KMNO4",3).toString());
    
    
    		/**
    		 * sha家族加密算法
    		 * sha1:aca1eb31d2dcf8f1fcf3fd7a7104232785afad41     40 位
    		 * sha256: 616a47d8e1e42f23693bb3a85749bf18d4b6e5380ddfd5717aafa61e33d5211e
    		 * sha384:84f5cbb18e2d9f1c81b8cec6f443a2b229993689a2ebae97db37e13af1dfb00ec6168713a53fe19d33a63d4d30889553
    		 * sha512:c3e5102b6a7ec6caa5b255dae2895b11c2ef0c7b9bfea8e848653372b53f3ef665d96ea283a21eac683cc0fe5c4b1f64692c2056a8a9636ee1931151043d2b5d
    		 */
    		System.out.println("sha1:"+new Sha1Hash("123456","lisi",2));
    		System.out.println("sha256:"+new Sha256Hash("123456","lisi",2));
    		System.out.println("sha384:"+new Sha384Hash("123456","lisi",2));
    		System.out.println("sha512:"+new Sha512Hash("123456","lisi",2));
    
    	}
    }
    

    1.2.8 编写密码比较器

    在这里插入图片描述

    public class BosCredentialsMatcher extends SimpleCredentialsMatcher {
    
        @Override
        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
            //向下转型
            UsernamePasswordToken upToken = (UsernamePasswordToken)token;
            //获取用户页面输入的密码
            String pwd = new String(upToken.getPassword());
            //加密
            String newPwd =Encrypt.md5(pwd, upToken.getUsername()).toString();
            //获取数据库密码
            String dbPwd = info.getCredentials().toString();
    
            return equals(newPwd, dbPwd);
        }
    }
    

    5 散列加密算法:加密的时候撒盐

    MD5:

    SHA:

    1.5.6 修改BosRealm

    在这里插入图片描述

    
    //自定义Realm ,实现安全数据 连接
    public class BosRealm extends AuthorizingRealm {
    
       @Resource
       private UserService userService;
    
       @Resource
       private RoleService roleService;
    
       @Resource
       private PermissionService permissionService;
    
       @Override
       public String getName() {
          return "bosRealm";
       }
       
       @Override
       // 认证...
       protected AuthenticationInfo doGetAuthenticationInfo(
             AuthenticationToken token) throws AuthenticationException {
          System.out.println("shiro 认证管理... ");
          //1 获得密码
          String username = (String)token.getPrincipal();
            
          //2 通过username从数据库中查找 User对象,如果找到,没找到.
          User user = userService.findUserByUsername(username);;
          if(user == null){
             //返回null表示账号不存在
              return null;
          }
    
          return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
       }
       
       @Override
       // 授权...
       protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
          System.out.println("shiro 授权管理...");
    
          SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
          // 根据当前登录用户 查询对应角色和权限
    //    User user = (User) SecurityUtils.getSubject().getPrincipal();
          User user = (User) pc.getPrimaryPrincipal();
          // 调用业务层,查询角色
          List<Role> roles = roleService.findByUser(user);
          for (Role role : roles) {
             authorizationInfo.addRole(role.getKeyword());
          }
          // 调用业务层,查询权限
          List<Permission> permissions = permissionService.findByUser(user);
          for (Permission permission : permissions) {
             authorizationInfo.addStringPermission(permission.getKeyword());
          }
    
          return authorizationInfo;
       }
    }
    

    1.5.7 确定权限不足时,显示未授权页面

    • 确定权限过滤器配置信息

    在这里插入图片描述

    • 确定页面

    在这里插入图片描述

    //拦截器.
    Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
    
    //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
    filterChainDefinitionMap.put("/login*", "anon");
    filterChainDefinitionMap.put("/validatecode.jsp*", "anon");
    filterChainDefinitionMap.put("/user/login*", "anon");
    filterChainDefinitionMap.put("/css/**", "anon");
    filterChainDefinitionMap.put("/js/**", "anon");
    filterChainDefinitionMap.put("/images/**", "anon");
    filterChainDefinitionMap.put("/services/**", "anon");
    filterChainDefinitionMap.put("/pages/base/courier**", "perms[courier:list]");
    filterChainDefinitionMap.put("/pages/base/area**", "roles[base]");
    //<!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
    //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
    filterChainDefinitionMap.put("/**", "authc");
    
    // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
    shiroFilterFactoryBean.setLoginUrl("/login.html");
    // 登录成功后要跳转的链接
    shiroFilterFactoryBean.setSuccessUrl("/index.html");
    //未授权界面;
    shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized.html");
    

    1.5.9 细粒度方法权限控制

    需求:张三可以查看快递员列表信息,但是无法添加快递员

    Shiro提供了若干注解用于在方法上进行权限控制

    注解 描述
    @RequiresPermissions() 用户必须具有指定【权限】,才可以访问被注解修饰的方法。
    @RequiresRoles() 用户必须具有指定【角色】,才可以访问被注解修饰的方法。

    在这里插入图片描述

    1539038644786

    • 步骤1:在授权方法中获取该用户的所有的权限:包括role权限和Permission权限
    • 步骤2:在运单 CourierController的添加方法上添加courier:list权限标识。

    在这里插入图片描述

    测试:无权限访问

    在这里插入图片描述

    1.6 动态菜单

    动态菜单: 不同用户登录后,应该看到不同菜单结构

    在这里插入图片描述

    1、 修改index.html 加载基本菜单 url路径

    // 基本功能菜单加载
    $.get("/menu/showMenu",function(data){
       $.fn.zTree.init($("#treeMenu"), setting, data);
    },"json");
    

    2、 在MenuController 添加 showMenu方法

    @RestController
    @RequestMapping("/menu")
    public class MenuController {
    
        @Autowired
        private MenuService menuService;
    
        // 加载左侧的菜单功能
        @GetMapping(value = "/showMenu")
        public ResponseEntity<List<Menu>>  showMenu(){
            // 调用业务层,查询当前用户具有菜单列表
            Subject subject = SecurityUtils.getSubject();
            User user = (User)subject.getPrincipal();
            // 查询菜单列表
            List<Menu> result = menuService.findByUser(user);
    
            return new ResponseEntity<List<Menu>>(result,HttpStatus.OK);
        }
    }
    

    3、 编写MenuService.java业务层

    @Service
    @Transactional
    public class MenuService {
    
        @Autowired
        private MenuMapper menuMapper;
    
        /**查询用户*/
        public List<Menu> findByUser(User user) {
            // 针对admin用户显示所有的菜单
            if(user.getUsername().equals("admin")){
                return menuMapper.selectAll();
            }
            else{
                // 使用用户ID,查询当前用户具有的菜单列表
                return menuMapper.findByUser(user.getId());
            }
        }
    }
    

    4、调用DAO

    @org.apache.ibatis.annotations.Mapper
    public interface MenuMapper extends Mapper<Menu> {
        @Select("select m.* from t_menu m,t_user u,t_user_role ur,t_role r,t_role_menu rm "
                + "where m.id = rm.menu_id and rm.role_id = r.id "
                + "and r.id = ur.role_id and ur.user_id = u.id "
                + "and u.id=#{id} order by m.priority")
        List<Menu> findByUser(Integer id);
    }
    
    展开全文
  • Shiro权限控制流程

    2020-11-22 00:43:57
    软件研发,权限控制是每个软件必备的功能,shiro权限控制框架比较成熟,控制流程看下图:
  • 主要介绍了Spring MVC整合Shiro权限控制,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • Shiro权限控制(三):Shiro注解权限验证

    千次阅读 2019-04-10 23:56:42
    在前面的一篇博文中《Shiro权限控制之自定义Filter(二)》,我们的权限验证是配置在shiro配置文件中的,即在spring-shiro-web.xml中的ShiroFilterFactoryBean的filterChainDefinitions属性中,如下 <!-- Shiro的...
  • shiro权限控制简介

    2019-07-10 16:35:13
    最近项目中用到了shrio权限控制,因此抽出一定的时间了解了一下他的使用方式和原理,同springsecurity的方式一样,使用shiro权限控制,主要包括了三个重要的组成部分 user,permission,role即用户,权限,和角色,...
  • 主要介绍了Springboot和bootstrap实现shiro权限控制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • Shiro权限控制

    2019-03-19 22:29:26
    使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用...项目中Shiro权限控制器的使用 导包(一) (二)resources中配置文件 在applicationContext.xml关联 web.xml中配置 (三)自定义一个类管理用...
  • vue+element+springboot+shiro权限控制,mysql数据库 登录帐号/密码 admin:111111
  • Shiro 权限控制标签示例 <div class="layui-inline my-float-right"> <@shiro.hasPermission name="batchOrder:save"> <button class="layui-bt...
  • Apache shiro权限控制基础配置代码,比网上其它文章全面,细致,适用,经测试可行.

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,112
精华内容 1,644
关键字:

shiro权限控制