springmvc+spring+mybatis+shrio+mysql

shrio的作用已经不用说了,就直接说用吧,当然我们的系统要求没有很高,所以我也学习的比较简单。
第一步:jar

<dependency>  
    <groupId>org.apache.shiro</groupId>  
    <artifactId>shiro-core</artifactId>  
    <version>1.2.2</version>  
</dependency>  

<dependency>  
    <groupId>org.apache.shiro</groupId>  
    <artifactId>shiro-web</artifactId>  
    <version>1.2.2</version>  
</dependency>  

<dependency>  
    <groupId>org.apache.shiro</groupId>  
    <artifactId>shiro-spring</artifactId>  
    <version>1.2.2</version>  
</dependency> 

第二步:配置web.xml

<context-param>
        <param-name>contextConfigLocation</param-name>
        <!--加载springmvc和spring配置文件的位置,添加sprin-          shiro.xml--!>
        <param-value>classpath:spring/applicationContext-*.xml,classpath:spring/spring-shiro.xml</param-value>
</context-param>
<filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
</filter>
<filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
</filter-mapping>

第三步:
spring-shrio.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
                        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"  
    default-lazy-init="true">  

    <description>Shiro Configuration</description>
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--设置自定义realm -->
        <property name="realms" ref="monitorRealm" />
          <!-- 缓存管理器 -->  
        <property name="cacheManager" ref="cacheManager"/> 
    </bean>
      <!-- 会话管理器 -->
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <!-- 设置超时时间 -->
        <property name="globalSessionTimeout" value="18000"/>
        <property name="deleteInvalidSessions" value="true"/>
        <property name="sessionValidationSchedulerEnabled" value="true"/>
        <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
        <property name="sessionDAO" ref="sessionDAO"/>
        <property name="sessionIdCookieEnabled" value="true"/>
        <property name="sessionIdCookie" ref="sessionIdCookie"/>
    </bean>
    <!--自定义Realm 继承自AuthorizingRealm -->
    <bean id="monitorRealm" class="com.yzb.loanplatform.manager.util.shrio.ShrioRealm">
      <property name="credentialsMatcher">
           <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
               <property name="hashAlgorithmName" value="MD5"/>
               <!--true means hex encoded, false means base64 encoded -->
               <property name="storedCredentialsHexEncoded" value="true"/>
               <!-- 迭代次数 -->
               <property name="hashIterations" value="1024" />
           </bean>
       </property>   
    </bean>

    <!-- Shiro主过滤器本身功能十分强大,其强大之处就在于它支持任何基于URL路径表达式的、自定义的过滤器的执行 --> 
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
       <property name="securityManager" ref="securityManager" />
        <!-- 要求登录时的链接,非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 -->  
        <property name="loginUrl" value="/login" />
        <!-- 用户访问未对其授权的资源时,所显示的连接 -->
        <!-- <property name="unauthorizedUrl" value="/error/noperms.jsp" /> -->
        <property name="filterChainDefinitions">
            <value>
                <!-- 
                    Anon:不指定过滤器
                    Authc:验证,这些页面必须验证后才能访问,也就是我们说的登录后才能访问。
                -->
                /login.ftl = anon
                /system/**= authc
                /orderInfo/findOrderInfo= authc
                /orderInfo/ucfRefundCallback= anon
                /static/**=anon
            </value>
        </property>
    </bean>

  <!-- 
    用户授权信息Cache   -->
    <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />  

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

   <!--  AOP式方法级权限检查   -->
  <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>  

第四步:springmvc中配置:其作用是在conroller中使用注解方法实现权限拦截

<!-- 为使用AOP注解方式控制权限 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
        <property name="proxyTargetClass" value="true" />
    </bean>

以上配置就是所有配置,现在仔细说下spring-shrio.xml中的几个重点:
1,自定义的realm:shrio中核心,有SecurityManager管理,

<bean id="securityManager"class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--设置自定义realm -->
        <property name="realms" ref="monitorRealm" />
</bean>
<!--自定义Realm 继承自AuthorizingRealm -->
<bean id="monitorRealm" class="**com.yzb.loanplatform.manager.util.shrio.ShrioRealm**">
      <property name="credentialsMatcher">
           <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
               <!-- 密码加密方式 -->
               <property name="hashAlgorithmName" value="MD5"/>
               <property name="storedCredentialsHexEncoded" value="true"/>
               <!-- 次数加密次数 -->
               <property name="hashIterations" value="1024" />
           </bean>
       </property>   
</bean>

重点来了,ShrioRealm.java用来实现登陆身份验证和首选操作;

package com.yzb.loanplatform.manager.util.shrio;

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

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
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 com.yzb.loanplatform.pojo.Employee;
import com.yzb.loanplatform.pojo.Permission;
import com.yzb.loanplatform.pojo.Role;
import com.yzb.loanplatform.service.EmployeeService;
import com.yzb.loanplatform.vo.EmployeeVo;

/**
 * @ClassName: ShrioRealm
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author zhaofq
 * @date 2017年8月10日
 */
public class ShrioRealm extends AuthorizingRealm {

    private static Logger loger = Logger.getLogger(ShrioRealm.class.getName());
    @Autowired
    private EmployeeService employeeService;

    /**
     * 授权操作
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 获取当前登录的用户名,等价于(String)principals.fromRealm(this.getName()).iterator().next()
        String account = (String) super.getAvailablePrincipal(principals);
        EmployeeVo employeeVo = new EmployeeVo();
        employeeVo.setPhone(account);
        List<String> roles = new ArrayList<String>();
        List<String> permissions = new ArrayList<String>();
        // //从数据库中获取当前登录用户的详细信息
        Employee employee = employeeService.getEmployeeRoleAanPermissions(employeeVo);
        if (employee != null) {
            // 实体类User中包含有用户角色的实体类信息
            if (employee.getRoleList() != null && employee.getRoleList().size() > 0) {
                // 获取当前登录用户的角色
                for (Role role : employee.getRoleList()) {
                    roles.add(role.getRoleCode());
                    // 实体类Role中包含有角色权限的实体类信息
                    if (role.getPermissionList() != null && role.getPermissionList().size() > 0) {
                        // 获取权限
                        for (Permission pmss : role.getPermissionList()) {
                            if (!StringUtils.isEmpty(pmss.getUrl())) {
                                permissions.add(pmss.getUrl());
                            }
                        }
                    }
                }
            }
        } else {
            throw new AuthorizationException();
        }
        // 为当前用户设置角色和权限
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(roles);
        info.addStringPermissions(permissions);
        return info;
    }

    /**
     * 身份验证操作
     */
    @SuppressWarnings("unused")
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
            throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        String userName = String.valueOf(token.getPrincipal()); // 得到身份(用户名)
        String passWord = new String((char[]) token.getCredentials()); // 得到认证/凭证(密码)
        // 从数据库中查询用户用信息
        EmployeeVo employeeVo = new EmployeeVo();
        employeeVo.setPhone(userName);
        Employee employee = employeeService.getEmployee(employeeVo);
        Employee employee2 = employeeService.getEmployeeRoleAanPermissions(employeeVo);

        if (employee != null) {
            if ("1".endsWith(employee.getStatus().toString())) { // 判断帐号是否锁定
                throw new LockedAccountException();// 抛出 帐号锁定异常
            }
            // 此处无需比对,比对的逻辑Shiro会做,我们只需返回一个和令牌相关的正确的验证信息
            byte[] myByte = employee.getSalt().getBytes();
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(employee.getPhone(),
                    employee.getEmpPassword(), ByteSource.Util.bytes(myByte), this.getName());
            // simpleAuthenticationInfo.setCredentialsSalt(salt);

            return simpleAuthenticationInfo;
        } else {
            // 没有返回登录用户名对应的SimpleAuthenticationInfo对象时,就会在LoginController中抛出UnknownAccountException异常
            return null;
        }
    }

    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }

    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }

    public void clearAllCachedAuthorizationInfo() {
        getAuthorizationCache().clear();
    }

    public void clearAllCachedAuthenticationInfo() {
        getAuthenticationCache().clear();
    }

    public void clearAllCache() {
        clearAllCachedAuthenticationInfo();
        clearAllCachedAuthorizationInfo();
    }
}

有两个重要的方法:
1,doGetAuthenticationInfo:用来做身份认证,当用户输入用户名密码后在登陆的controller中通过:

UsernamePasswordToken token = new UsernamePasswordToken(username,password);
            Subject currentUser = SecurityUtils.getSubject();  
            token.setRememberMe(true);  
            currentUser.login(token);

实现对doGetAuthenticationInfo的调用,具体作用就是把页面发送过来的用户名和密码传入到doGetAuthenticationInfo方法中,
使用用户名去重是否有对应的用户,如果找到就调用shrio内部的密码校验方法,

// 此处无需比对,比对的逻辑Shiro会做,我们只需返回一个和令牌相关的正确的验证信息
            byte[] myByte = employee.getSalt().getBytes();
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(employee.getPhone(),
                    employee.getEmpPassword(), ByteSource.Util.bytes(myByte), this.getName());
            // simpleAuthenticationInfo.setCredentialsSalt(salt);

            return simpleAuthenticationInfo;

这里你只需要把你你查询数据获取到的用户名,密码,言放到SimpleAuthenticationInfo就可以了,可以看看看看源码是怎么实现的校验的,不再多说,
根据返回校验结果就会提示是否校验通过。
2,授权方法:doGetAuthorizationInfo:就是查看操作用户拥有的资源权限,
主要是根据用户名获取对应角色,权限的资源。
次方法调用时在controller总通过注解的方式:
@RequiresRoles(logical=Logical.OR,value={“admin”,”adminEmp”})
页面时通过shrio标签。

<shiro:hasAnyRoles name="admin,adminEmp" ><li><a href="${request.contextPath}/employee/employeePage">员工管理</a></li></shiro:hasAnyRoles>

当然我这里是把菜单写死了,所以不能控制菜单是否显示,而是在操作时提示没有权限,
原理大概是党操作菜单之后就会通过@RequiresRoles查看当强用户是否用户此角色,角色是否用户权限。
当也有直接控制按钮的方式我就不在说了,

public class BaseController {
    /**
     * 登录认证异常
     */
    @ExceptionHandler({ UnauthenticatedException.class, AuthenticationException.class })
    public String authenticationException(HttpServletRequest request, HttpServletResponse response) {
        if (WebUtilsPro.isAjaxRequest(request)) {
            // 输出JSON
            Map<String,Object> map = new HashMap<>();
            map.put("code", "-999");
            map.put("message", "未登录");
            return null;
        } else {
            return "redirect:/system/login";
        }
    }

    /**
     * 权限异常
     */
    @ExceptionHandler({ UnauthorizedException.class, AuthorizationException.class,ServletException.class })
    public String authorizationException(HttpServletRequest request, HttpServletResponse response) {
        DataResponse data = new DataResponse();
            // 输出JSON
            Map<String,Object> map = new HashMap<>();
            map.put("code", "-999");
            map.put("msg", "无权限");
            data.setCode(200);
            data.setData(map);
            return "/notPermission";
    }

}

登陆异常和无权限时提示,让每个使用权限的controller继承此controler就可以了,
最后角色权限表:

DROP TABLE IF EXISTS `MTB_ROLE`;
CREATE TABLE `MTB_ROLE` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  `description` varchar(50) DEFAULT NULL,
  `roleCode` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;
DROP TABLE IF EXISTS `MTB_PERMISSION`;
CREATE TABLE `MTB_PERMISSION` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `description` varchar(200) DEFAULT NULL,
  `url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4;
DROP TABLE IF EXISTS `MTB_ROLE_PERMISSION`;
CREATE TABLE `MTB_ROLE_PERMISSION` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_Id` int(36) DEFAULT NULL,
  `permission_Id` int(36) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4;
DROP TABLE IF EXISTS `MTB_EMPLOYEE_ROLE`;
CREATE TABLE `MTB_EMPLOYEE_ROLE` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `employee_Id` int(36) DEFAULT NULL,
  `role_Id` int(36) DEFAULT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8mb4;

还有一个就是员工表了,就
补充1: 登陆之后显示用户名字,在页面用<@shiro.principal/>就可以获得当强登陆用户名,当然也可以用${username}单这个是用了controller返回的对象的mv.addObject(“username”, username);推荐用<@shiro.principal/>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值