SpringMVC+Mybatis+Mysql+Shiro
来源:互联网 发布:windows chakan elf 编辑:程序博客网 时间:2024/06/05 03:44
shiro权限框架
- shiro主要的组件:
- SecurityManager:典型的Facade模式(该模式为子系统的各个类或方法提供一个简明一致的界面,隐藏子系统的复杂性,使子系统更加容易使用。)是Authenticator + Authorizer + SesssionFactory + CacheManager
- Authenticator:登录认证,进行用户名和密码的匹配。
- Authorizer:授权,即权限验证,验证某个已经通过认证(登录过的人)是否拥有某个权限。验证某个用户是否有相应的角色(管理员,普通人员等,这是粗粒度的验证),还可验证某个用户对某个资源是否具有权限(比如对删除按钮执行权的权限)。
- SessionManager:会话管理,用户登录后就是一次会话,相当于JavaWeb中的Session。但是此处的Session既可在JavaSE下使用也可用在JavaWeb中。
- Cryptography:密码加密。
- Caching:缓存,用户登录后,其拥有的角色或权限不必每次去查,这样可以提高效率。
- Concurrency:shiro支持多线程应用的并发执行,即如在一个线程中开启另一个线程,能把权限自动传播过去。
- Remember Me:记住无,这是个非常常见的功能,即一次登录后,下次再来的话就不用登录了。
shiro安全模型(subject–>SecurityManage–>Realm)
- shiro安全模型
- Subject:安全领域术语,除了代表人还可以是其他任何和当前应用交互的东西。手机号,邮箱登录等都可看作是subject。所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager,SecurityManager才是真正的执行者。
- SecurityManager:shiro的核心,它负责和其他组件交互。
- Realm:Shiro从Realm获取安全数据(如用户、角色、权限),SecurityManager要验证用户身份,那么他需要从Realm获取相应的用户进行比较以确定用户身份是否合法;从Realm中进行和数据库交互,得到相应的数据。
shiro身份认证流程
- 认证流程
- 1. 首先调用Subject.login(Token)进行登录,其会自动委托给SecurityManage,调用之前必须通过SecurityUtils.setSecurityManager()设置;
- 2. SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行什么验证;
- 3. Authenticatoe才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;
- 4. Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;
- 5. Authenticator会把相应的token传入Relam,从Realm获取什么验证信息,如果没有返回/抛出异常表示什么验证失败了,此处可以配置多个Realm,将按照相应的顺序及策略进行访问。
代码实现登录认证和权限认证
- 数据表设计:用户表,角色表,权限表,用户角色表,角色权限表
--用户表 DROP TABLE IF EXISTS `a_user`;CREATE TABLE `a_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(20) CHARACTER SET utf8 NOT NULL, `password` varchar(32) CHARACTER SET utf8 NOT NULL, `age` int(2) DEFAULT NULL, `gender` int(2) DEFAULT NULL, `mail` varchar(30) CHARACTER SET utf8 DEFAULT NULL, `tel` varchar(11) CHARACTER SET utf8 DEFAULT NULL, `image` varchar(50) CHARACTER SET utf8 DEFAULT NULL, `limits` int(2) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`)) ENGINE=InnoDB AUTO_INCREMENT=45 DEFAULT CHARSET=latin1;--角色表DROP TABLE IF EXISTS `role`;CREATE TABLE `role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(60) NOT NULL, `type` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;--权限表DROP TABLE IF EXISTS `permission`;CREATE TABLE `permission` ( `id` int(11) NOT NULL, `url` varchar(200) NOT NULL, `name` varchar(200) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;--用户角色表DROP TABLE IF EXISTS `user_role`;CREATE TABLE `user_role` ( `uid` varchar(11) NOT NULL, `rid` int(11) NOT NULL, PRIMARY KEY (`uid`,`rid`), KEY `kd_roleId` (`rid`), CONSTRAINT `fk_userId` FOREIGN KEY (`uid`) REFERENCES `a_user` (`username`) ON DELETE CASCADE, CONSTRAINT `kd_roleId` FOREIGN KEY (`rid`) REFERENCES `role` (`id`) ON DELETE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8;--角色权限表DROP TABLE IF EXISTS `role_permission`;CREATE TABLE `role_permission` ( `rid` int(11) NOT NULL, `pid` int(11) NOT NULL, PRIMARY KEY (`rid`,`pid`), KEY `fk_permisssionId` (`pid`), CONSTRAINT `fk_permisssionId` FOREIGN KEY (`pid`) REFERENCES `permission` (`id`) ON DELETE CASCADE, CONSTRAINT `fk_roleId` FOREIGN KEY (`rid`) REFERENCES `role` (`id`) ON DELETE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2. web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- 配置log4j --> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>classpath:log4j.properties</param-value> </context-param> <!-- 60秒检测日志配置 文件变化 --> <context-param> <param-name>log4jRefreshInterval</param-name> <param-value>60000</param-value> </context-param> <!--配置shiroFilter,让其过滤所有的url请求。--> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 --> <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> <listener> <description>spring监听器</description> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 防止spring内存溢出监听器 --> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener> <!-- SpringMVC核心分发器 --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></web-app>
3. 配置applicationContext.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" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"> <!-- springMvc的配置 --> <mvc:annotation-driven></mvc:annotation-driven> <!-- 开启注解配置Controller --> <context:component-scan base-package="com.itdage.*"></context:component-scan> <!-- 配置viewResolver --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"></property> <property name="suffix" value=".jsp"></property> </bean> <!-- Mybatis的配置 --> <import resource="classpath:mybatisConfig.xml"/> <!-- shiro的配置 --> <import resource="classpath:shiroConfig.xml"/></beans>
4. 配置shiroConfig.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" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"> <!--shiro默认会使用Servlet容器的Session,可通过sessionMode属性来指定使用Shiro原生Session--> <!--这里主要是设置自定义的单Realm应用,若有多个Realm可使用realms属性来替代 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="myRealm" /> <!-- 使用配置的缓存管理器 --> <property name="cacheManager" ref="cacheManager"></property> <!-- 会话管理 --> <property name="sessionManager" ref="sessionManager" /> </bean> <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" /> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!-- session的失效时长,单位毫秒 --> <property name="globalSessionTimeout" value="600000"/> <!-- 删除失效的session --> <property name="deleteInvalidSessions" value="true"/> </bean> <!--自己定义的Realm --> <bean id="myRealm" class="com.itdage.realms.ShiroDbRealm"> <!-- <property name="credentialsMatcher"> <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="MD5"></property> </bean> </property>--> </bean> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!--开启shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描Shiro注解的类 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor" /> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean> <!-- 启用shiro授权注解拦截方式 --> <!--Shiro主过滤器本身功能十分强大,其强大之处就在于它支持任何基于URL路径表达式的、自定义的过滤器的执行 --> <!--Web应用中,Shiro可控制的Web请求必须经过Shiro主过滤器的拦截,Shiro对基于Spring的Web应用提供了完美的支持 这里id的名字必须和web.xml中配置的一样,否则启动报错 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 装配securityManager,这个属性是必须的 --> <property name="securityManager" ref="securityManager" /> <!-- 配置登录页,非必须属性,默认会自动寻找Web工程根目录下的/login.jsp页面 --> <property name="loginUrl" value="/login.jsp" /> <!-- 配置登录成功后的页面,可以不写,登录成功后在Controller中已经指定了 --> <property name="successUrl" value="/list.jsp" /> <property name="unauthorizedUrl" value="/unauthorized.jsp" /> <!--下面value值得第一个'/'代表的路径是相对于HttpServletRequest.getContextPath()的值来的 --> <!--anon:可以被匿名访问 --> <!--authc:必须认证(登录)后才能访问 --> <!--格式:url=拦截器[参数],拦截器[参数] url模式使用Ant风格模式:Ant路径通配符支持?、*、**通配符不包括分隔符"/" -?:匹配一个字符: 如/admin?将匹配/admin1,但不匹配/admin或/admin/ -*:匹配0或多个字符串,如/admin*将匹配/admin、/admin123但不匹配/admin/123 -**:匹配路径中的0或多个路径,如/admin/**将匹配/admin/a或/admin/a/b --> <!--URL权限采取第一次匹配优先的方式,即从头开始使用第一个匹配url模式的拦截器链 如: /bb/** = filter1 /bb/aa = filter2 /** = filter3 如果请求的url是/bb/aa,因为按照声明顺序进行匹配,那么将使用filter1进行拦截。 --> <property name="filterChainDefinitions"> <value> <!-- 静态资源允许访问 --> <!-- 登录页允许访问 --> /login.jsp = anon /test/login = anon /logout = logout <!-- 其他资源都需要认证 --> /** = authc </value> </property> </bean></beans>
5. Java代码
//验证登录的Controllerpackage com.itdage.controller;import java.io.IOException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.IncorrectCredentialsException;import org.apache.shiro.authc.UnknownAccountException;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.authz.annotation.RequiresPermissions;import org.apache.shiro.subject.Subject;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import com.itdage.entity.User;import com.itdage.serviceImpl.UserServiceImpl;@Controller@RequestMapping("/test")public class ShiroLogin { /** * 验证登录 * @param user * @param request * @return */ @RequestMapping(value = "/login", method = RequestMethod.POST) public String login(User user, HttpServletRequest request){ if(isRelogin(user)){ //已经登录过了,跳转到fail.jsp页面 return "fail"; } HttpSession session = request.getSession(); session.setAttribute("successMsg", "登录成功!"); return shiroLogin(user); } /** * 判断用户是否已经登录 * @param user * @return */ public boolean isRelogin(User user){ Subject subject = SecurityUtils.getSubject(); if(subject.isAuthenticated()){ return true; //参数未改变,无需重新登录,默认为已登录成功 } return false; } /** * 利用shiro进行登录 * @param user * @return */ public String shiroLogin(User user){ UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword().toCharArray(), null); token.setRememberMe(true); try{ SecurityUtils.getSubject().login(token); }catch(UnknownAccountException e){ System.out.println(e.getMessage()); return "用户不存在或密码不正确1"; }catch(IncorrectCredentialsException e){ System.out.println(e.getMessage()); return "用户名或密码不正确2"; }catch(AuthenticationException e){ System.out.println(e.getMessage()); return e.getMessage(); } return "redirect:/list.jsp"; } @RequestMapping("/logout") public void logout(HttpServletRequest request, HttpServletResponse response) throws IOException{ Subject subject = SecurityUtils.getSubject(); if(subject != null){ try{ subject.logout(); System.out.println("用户退出"); }catch(Exception e){ System.out.println("退出异常"); } } response.sendRedirect("/shiro-web3/login.jsp"); }}
//自定义的Realmpackage com.itdage.realms;import java.util.List;import org.apache.shiro.SecurityUtils;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.authc.UsernamePasswordToken;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.authz.SimpleAuthorizationInfo;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.session.Session;import org.apache.shiro.subject.PrincipalCollection;import org.springframework.beans.factory.annotation.Autowired;import com.itdage.entity.Permission;import com.itdage.entity.Role;import com.itdage.entity.User;import com.itdage.serviceImpl.UserServiceImpl;public class ShiroDbRealm extends AuthorizingRealm{ @Autowired private UserServiceImpl userService; /* * 授权 * 授权查询回调函数,进行权限鉴定但缓存中无用户的授权信息时回调,负责在应用程序中决定用户的访问控制的方法。 */ @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { String username = (String) principals.getPrimaryPrincipal(); User user = new User(); user.setUsername(username); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); List roleList = userService.getRole(user); if(roleList != null){ for (Role role : roleList) { List permissions = userService.getPermissions(role.getName()); for (Permission permission2 : permissions) { authorizationInfo.addStringPermission(permission2.getUrl()); } authorizationInfo.addRole(role.getName()); } return authorizationInfo; } return null; } /* * 验证登录 * 认证回调函数,登录信息和用户验证信息验证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { User userLogin = tokenToUser((UsernamePasswordToken)token); User user = userService.login(userLogin); if(user == null){ return null;//异常处理,找不到数据 } return new SimpleAuthenticationInfo(userLogin.getUsername(), userLogin.getPassword(), getName()); } private User tokenToUser(UsernamePasswordToken token) { User user = new User(); user.setUsername(token.getUsername()); user.setPassword(String.valueOf(token.getPassword())); return user; }}
//DAO层package com.itdage.dao;import java.util.List;import org.springframework.stereotype.Repository;import com.itdage.entity.Permission;import com.itdage.entity.Role;import com.itdage.entity.User;@Repositorypublic interface UserDao { public User login(User user); public List getRole(User user); public List getPermissions(String role);}
阅读全文
0 0
- SpringMVC+Mybatis+Mysql+Shiro
- shiro+springmvc+mybatis
- Shiro+SpringMVC+MyBatis整合
- springMVC+shiro+mybatis
- spring+springmvc+mybatis shiro权限管理系统demo mysql数据库
- druid springmvc mybatis bootstrap shiro
- shiro+springmvc+mybatis(2)
- springmvc+mybatis+shiro+maven开篇
- sping+springmvc+mybatis+shiro,配置文件
- Spring+SpringMVC+Mybatis+Shiro+Msql
- 发布一个demo maven+freemarker+shiro+springmvc+spring+mybatis+redis+mysql
- springmvc-spring-mybatis-shiro-easyui权限系统
- javaEE 后台框架 SpringMVC Mybatis Shiro druid
- 分布式 Springmvc myBatis shiro restful dubbo zookeepe
- java框架整合Springmvc+mybatis+shiro+bootstrap
- shiro-springmvc-mybatis登录认证 权限控制
- Maven+SpringMVC+MyBatis+Shiro整合开发
- springmvc mybatis 整合 bootstrap maven shiro druid
- 笔记
- Windows无法访问Ubuntu Samba 解决方案
- Python自学之路第三步——列表的增删改查
- 【C语言】猴子选大王问题
- 软件测试作业进度-2
- SpringMVC+Mybatis+Mysql+Shiro
- Java中Synchronized的用法
- PathView的用法
- 利用堆栈解析算术表达式一:基本过程
- ThreadLocal类
- ACM-11月16日周四周中训练心得
- 5.ES6 const命令
- 揭秘java日志系统
- android 已安装存在签名冲突的同名数据包