shiro小结

从本篇起,开始做一些日常工作小结。由于本人依旧属于刚入门级别,所以做出的一些总结难免会有一些不严谨,甚至是错误;但本人并不想误人子弟,只是想记录的轨迹,仅此而已。
使用shiro,先要导入相关jar包

使用IDEA的,且创建的是maven工程的,可直接在pom.xml引入

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.2.2.RELEASE</version>
        </dependency>


shiro和spring的整合配置如下:

 

<?xml version="1.0" encoding="UTF-8"?>
<!--suppress ALL, SpringModelInspection -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.1.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd">

    <!-- 配置SecurityManager、自定义Realm、定义加密算法、自定义二级缓存 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm"/>
        <property name="rememberMeManager" ref="rememberMeManager"/>
    </bean>

    <!-- 配置自定义第三方缓存EhCache -->
    <bean id="shiroCache" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/>
    </bean>

    <!-- 自定义的realm -->
    <bean id="myRealm" class="com.jz.xd.service.shiro.MyRealm">
        <!-- 添加自定义检验规则 -->
        <property name="credentialsMatcher" ref="passwordMatcher"/>
    </bean>

    <!-- remenberMe配置 -->
    <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <constructor-arg value="rememberMe"/>
        <property name="httpOnly" value="true"/>
        <!-- 默认记住7天(单位:秒) -->
        <property name="maxAge" value="604800"/>
    </bean>
    <!-- rememberMe管理器 -->
    <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
        <property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/>
        <property name="cookie" ref="rememberMeCookie"/>
    </bean>

    <!-- 自定义加密算法 -->
    <bean id="passwordMatcher" class="com.jz.xd.service.shiro.CustomCredentialsMatcher"/>

    <!-- 过滤URL,filter。这个id名称必须和web.xml中声明的filter一致 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>

        <!--登录失败之后指向的url-->
        <property name="loginUrl" value="/index/toLogin"/>
        <!--无权限指向的页面-->
        <property name="unauthorizedUrl" value="/index/toLogin"/>
        <property name="filterChainDefinitions">
            <!-- 哪些jsp,action等其他资源可以放行,哪些jsp,action不能放行。配置时按先后顺序进行url过滤 -->
            <value>
                <!--权限规则-->
                <!--anon:例子/admins/**=anon 没有参数,表示可以匿名使用。-->
                <!--authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数-->
                <!--roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。-->
                <!--perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。-->
                <!--rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。-->
                <!--port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。-->
                <!--authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证-->
                <!--ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https-->
                <!--user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查-->

                <!--静态资源和登录页不拦截-->
                /index.jsp =anon
                /html/**=anon
                /resources/** =anon

                <!--对每个Controller做拦截-->
                /index/**=anon

                /login/toInformation=user
                <!--...-->

                /login/toShopCar=authc
                <!--...-->

                /root/upload/toUploadVideo=authc,perms[/root/upload/toUploadVideo]
                <!--...-->
            </value>
        </property>
    </bean>


    <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!-- 生成代理,通过代理进行控制 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor">
        <property name="proxyTargetClass" value="true"/>
    </bean>

    <!-- 安全管理器 -->
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

</beans>

后一个/root/upload/toUploadVideo是权限名称,可以自己重命名,这里直接用拦截的路径。

 

其中:

package com.jz.xd.service.shiro;

import com.jz.xd.model.SPermission;
import com.jz.xd.model.Srole;
import com.jz.xd.model.User;
import com.jz.xd.service.IndexService;
import com.jz.xd.service.RootService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by HARU on 2017/3/17.
 */
public class MyRealm extends AuthorizingRealm {
    @Autowired
    private IndexService indexService;
    @Autowired
    private RootService rootService;

    private static Logger logger = LogManager.getLogger(MyRealm.class);

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("权限");
        User user = (User) principalCollection.getPrimaryPrincipal();
        if (user != null) {
            User userEntity = indexService.getUser(user.getUsername());
            List<String> roles = new ArrayList<String>();
            List<String> permissions = new ArrayList<String>();
            List<Srole> role = rootService.getRole(userEntity);
            for (Srole srole : role) {
                roles.add(srole.getRolename());
                List<SPermission> perms = indexService.getPerms(srole);
                for (SPermission sPermission : perms) {
                    if (!StringUtils.isEmpty(sPermission.getUrl())) {
                        permissions.add(sPermission.getUrl());
                    }
                }
            }
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            //添加角色信息
            simpleAuthorizationInfo.addRoles(roles);
            //添加权限信息
            simpleAuthorizationInfo.addStringPermissions(permissions);
            return simpleAuthorizationInfo;
        } else {
            return null;
        }
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        logger.debug("验证登录");
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        //获取前台传入的username
        String username = usernamePasswordToken.getUsername();
        if (!StringUtils.isEmpty(username)) {
            //如果用名不为空
            User userEntity = indexService.getUser(username);
            if (userEntity == null) {
                userEntity = indexService.getUserByPhone(username).get(0);
            }
            if (userEntity != null) {
                return new SimpleAuthenticationInfo(userEntity, userEntity.getPassword(), this.getName());
            }
        }
        return null;
    }
}

 

 

package com.jz.xd.service.shiro;

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;

/**
 * Created by HARU on 2017/3/17.
 */
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        UsernamePasswordToken usertoken = (UsernamePasswordToken) token;

        //注意token.getPassword()拿到的是一个char[],不能直接用toString(),它底层实现不是我们想的直接字符串,只能强转
        Object tokenCredentials = Encrypt.md5hash(String.valueOf(usertoken.getPassword()), Encrypt.SALT);
        Object accountCredentials = getCredentials(info);

        //将密码加密与系统加密后的密码校验,内容一致就返回true,不一致就返回false
        return equals(tokenCredentials, accountCredentials);
    }
}

 

package com.jz.xd.service.shiro;

import org.apache.shiro.crypto.hash.Md5Hash;

/**
 * Created by HARU on 2017/3/17.
 */
public class Encrypt {
    public static final String SALT = "HARU";

    /*
     * 两个参数:1)需要加密密码,2)盐(混淆、动态值) 用户名 3)hash次数
     *
     * 例如:
     * password=123456+“123”、“abc”
     */
    public static String md5hash(String password, String salt) {
        return new Md5Hash(password, salt, 2).toString();
    }

    public static void main(String[] args) {
        System.out.println(Encrypt.md5hash("root", SALT));
    }
}


同时,web.xml中要添加:

 

 

<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-service.xml,classpath:spring-shiro.xml</param-value>
</context-param>
<!--shiro 权限-->
<filter>
	<filter-name>shiroFilter</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
	<filter-name>shiroFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

 

jsp文件上要想使用shiro标签,先引入<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<shiro:guest></shiro:guest>标签代表未登录时游客的访问,可以看到的内容。
 <shiro:user> </shiro:user>标签代表登录以后可以看到的内容,<shiro:principal property="username"/>可以在登录以后显示用户名。
之后在进行登录的逻辑时:

	Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
        if (rememberMe == null) {
            rememberMe = false;
        }
        usernamePasswordToken.setRememberMe(rememberMe);
        subject.login(usernamePasswordToken);
        Object object = subject.getPrincipal();
        //把信息放在Shiro的session中
        //shiro默认的session就是httpSession
        subject.getSession().setAttribute("user", users.get(0));


注销:SecurityUtils.getSubject().logout();

 

 

补充:关于权限角色。

可以先创建几张表:

CREATE TABLE `role_perm` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色权限ID',
  `roleId` int(11) DEFAULT NULL COMMENT '角色ID',
  `permId` int(11) DEFAULT NULL COMMENT '权限ID',
  PRIMARY KEY (`id`)
)

CREATE TABLE `sys_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
  `roleName` varchar(255) DEFAULT NULL COMMENT '角色名',
  `roleDescription` varchar(255) DEFAULT NULL COMMENT '角色描述',
  PRIMARY KEY (`id`)
)

CREATE TABLE `sys_permission` (
  `prid` int(11) NOT NULL AUTO_INCREMENT COMMENT '权限ID',
  `modelName` varchar(255) DEFAULT NULL COMMENT '拦截名称',
  `url` varchar(255) DEFAULT NULL COMMENT '拦截的路径',
  `parentId` int(11) DEFAULT NULL COMMENT '父Id',
  PRIMARY KEY (`prid`)
)

CREATE TABLE `user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户权限ID',
  `uid` int(11) DEFAULT NULL COMMENT '用户iD',
  `roleId` int(11) DEFAULT NULL COMMENT '角色ID',
  PRIMARY KEY (`id`)
)

 

shiro角色权限的一个应用是在后台管理系统中,侧边栏的操作列表:

可以将一级列表设为父权限,同时也是角色名,这样表的数据如下:

权限表:

角色表(图1):

并且创建两个实体:

 

public class Pmenu {
    private String pid;
    private String pMenuName;
    private String url;
    private List<Cmenu> cmenus;

    public String getPid() {
        return pid;
    }

    public void setPid(String pid) {
        this.pid = pid;
    }

    public String getpMenuName() {
        return pMenuName;
    }

    public void setpMenuName(String pMenuName) {
        this.pMenuName = pMenuName;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public List<Cmenu> getCmenus() {
        return cmenus;
    }

    public void setCmenus(List<Cmenu> cmenus) {
        this.cmenus = cmenus;
    }
}

 

 

package com.jz.xd.model;

/**
 * Created by HARU on 2017/6/16.
 */
public class Cmenu {
    private String cid;
    private String cMenuName;
    private String url;

    public String getCid() {
        return cid;
    }

    public void setCid(String cid) {
        this.cid = cid;
    }

    public String getcMenuName() {
        return cMenuName;
    }

    public void setcMenuName(String cMenuName) {
        this.cMenuName = cMenuName;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

 


这样在加入后台时,获取权限角色列表,用mybatis:

 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.jz.xd.mapper.ext.PermissionMapperExt">

    <!--id用于合并结果相关的记录-->
    <resultMap id="mune" type="com.jz.xd.model.Pmenu">
        <id property="pid" column="pid"/>
        <result property="pMenuName" column="pname"/>
        <result property="url" column="purl"/>
        <collection property="cmenus" ofType="com.jz.xd.model.Cmenu">
            <id property="cid" column="cid"/>
            <result property="cMenuName" column="cname"/>
            <result property="url" column="curl"/>
        </collection>
    </resultMap>

    <select id="getMenu" resultMap="mune">
        SELECT
        sp_a.prid cid,
        sp_a.modelName cname,
        sp_a.url curl,
        sp_b.modelName pname,
        sp_b.prid pid,
        sp_b.url purl
        FROM
        roleperm srp
        LEFT JOIN s_permission sp_a ON srp.permId = sp_a.prid
        LEFT JOIN s_permission sp_b ON sp_b.prid = sp_a.parentId
        WHERE
        <if test="rIds!=null">
            srp.roleId IN
            (
            <foreach collection="rIds" index="index" item="rId" separator=",">
                #{rId}
            </foreach>
            )
        </if>
        /*使用order by field(roleId,5,1)会将IN中的条件按这样的顺序查询,否则默认是按roleId递增查询*/
    </select>
</mapper>

 

mysql查询出的是:


注意,使用这样就能体现resultMap的好处,设置了ID就可以保证角色的唯一,即使在添加角色时,就如图1 ,添加了id为1,5的角色,即使名字都是上传,但是,进过连接得到的权限名都会映射到同一个父权限名“上传”,且pid是相同的,所有也只会在列表上显示一个“上传”;且子权限也会同上一样,结果合并且只显示一个。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值