shiro小结

来源:互联网 发布:瑞士胡椒盐包带淘宝 编辑:程序博客网 时间:2024/06/05 17:50
从本篇起,开始做一些日常工作小结。由于本人依旧属于刚入门级别,所以做出的一些总结难免会有一些不严谨,甚至是错误;但本人并不想误人子弟,只是想记录的轨迹,仅此而已。
使用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是相同的,所有也只会在列表上显示一个“上传”;且子权限也会同上一样,结果合并且只显示一个。



原创粉丝点击