首先在shiro的框架权控制感觉还行,有注解模式下的 注解模式是业务的权限控制,就是访问不到service层,另一种是jsp的标签权限控制
这样感觉耦合度有点高,最好的方式是两个模式混搭
首先配置pom.xml的jar包
<!-- spring jar包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <!-- AOP注解的包 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.8</version> </dependency> <!-- mybatis jar包 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.30</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.2.2</version> </dependency> <!-- JSTL标签类 --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- 日志文件管理包 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <!-- 格式化对象,方便输出日志 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.1.41</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <!-- 映入JSON --> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.13</version> </dependency> <!-- 上传组件包 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.9</version> </dependency> <!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.4.0</version> </dependency>
web.xml配置 配置web是有规则的 加载顺序ServletContext -- context-param -- listener -- filter -- servlet
<?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:spring-mybatis.xml,classpath:spring-shiro.xml</param-value> </context-param> <!-- Spring 监听 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 监听 内存泄露 配置在Spring监听前面--> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener> <!-- 编码过滤器 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <async-supported>true</async-supported> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- shiro 配置 --> <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 MVC servlet --> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:spring-mvc.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
shiro核心的地方在
<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>
配置基本的springmvc模式和mybatis
然后配置shiro配置
<?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"> <!-- 配置securityManager --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="cacheManager" ref="cacheManager" /> <property name="realm" ref="realm" /> </bean> <!-- 配置 CacheManager. 需要加入 ehcache 的 jar 包及配置文件. --> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> </bean> <!-- realm --> <bean id="realm" class="com.yuanxinbuluo.util.tool.Realm"> <!-- 使用密码加密 ,如果不使用密码加密可以去掉 --> <property name="credentialsMatcher"> <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="SHA1"></property> <property name="hashIterations" value="1024"></property> </bean> </property> </bean> <!-- ID与web.xml的配置filter-name名称一致 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <property name="loginUrl" value="/jsp/login.jsp"/> <property name="successUrl" value="/jsp/index.jsp" /> <property name="unauthorizedUrl" value="/jsp/error.jsp"/> <!-- 配置哪些页面需要受保护. 以及访问这些页面需要的权限. 1). anon 可以被匿名访问 2). authc 必须认证(即登录)后才可能访问的页面. 3). logout 登出. 4). roles 角色过滤器 --> <property name="filterChainDefinitions"> <value> /jsp/login.jsp = anon /user/login = anon /user/logout = logout /jsp/admin.jsp = roles[user] /user/findById = roles[admin] /** = authc </value> </property> </bean> <!-- 生命周期 可以自动的调用在spring shiro的bean的生命周期 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- 启用注解 必须配置lifecycleBeanPostProcessor才能使用 --> <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> </beans>
shiro我在定义realm是一个自定义的类 这个类继承了 AuthorizingRealm 这个有两个方法
doGetAuthorizationInfo(授权)方法和doGetAuthenticationInfo(登录验证)方法
然后可以泡一下项目,如果运行出现shiroFilter这个bean没有找到这个原因有两点
1.web.xml定义file-name的名称要和 这个shiro配置文件中的org.apache.shiro.spring.web.ShiroFilterFactoryBean这个类的名称一致
2.配置文件没有加载进去,如果web.xml写了加载shiro的配置 仔细看一下,在错误地方打一个断点,错误的提示就会提示 classpath:spring-shiro.xml未找到
说明了还是没有加载进去,这种情况 你可能加载的配置文件 如 mybatis.xml配置,redis.xml配置等加载顺序调整一下
如果运行正常,建立页面
import java.util.HashSet; import java.util.Set; import javax.annotation.Resource; 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.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import com.yuanxinbuluo.mode.User; import com.yuanxinbuluo.service.IUserService; /** * SHIRO realm * */ public class Realm extends AuthorizingRealm { /** * 加密方式 */ public final static String HASHALGORITHMNAME = "SHA1"; /** * 加密次数 */ public final static Integer HASHITERATIONS = 1024; /** * 用户 */ @Resource private IUserService userService; /** * 授权 */ @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { //1. 从 PrincipalCollection 中来获取登录用户的信息 User user = (User) principals.getPrimaryPrincipal(); Set<String> roles = new HashSet<String>(); //2. 利用登录的用户的信息来用户当前用户的角色或权限(可能需要查询数据库) if(user.getUserPhone().equals("123456")){
//授权user角色 roles.add("user"); } //3. 创建 SimpleAuthorizationInfo, 并设置其 reles 属性. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles); return info; } /** * 用户验证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { //1. 把 AuthenticationToken 转换为 UsernamePasswordToken UsernamePasswordToken upToken = (UsernamePasswordToken) token; //2. 从 UsernamePasswordToken 中来获取 username String username = upToken.getUsername(); //3.从数据库查询用户 User user = userService.login(username); if(user == null){ //抛出异常 throw new UnknownAccountException("用户不存在!"); } //6. 根据用户的情况, 来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo //以下信息是从数据库中获取的. //1). principal: 认证的实体信息. 可以是 username, 也可以是数据表对应的用户的实体类对象. //2). credentials: 密码. //3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可 //4). 盐值. 改变两次密码不一样 可以放入随机数 ByteSource credentialsSalt = ByteSource.Util.bytes(username); //加密 Object result = new SimpleHash(Realm.HASHALGORITHMNAME, user.getUserPwd(), credentialsSalt, HASHITERATIONS); AuthenticationInfo authcInfo ; authcInfo = new SimpleAuthenticationInfo(user, result, credentialsSalt, super.getName()); return authcInfo; } }
接下来的是控制器
import java.util.Map; import javax.annotation.Resource; 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.UnauthorizedException; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.view.InternalResourceViewResolver; import com.yuanxinbuluo.service.IUserService; import com.yuanxinbuluo.util.model.RequestHttpCode; import com.yuanxinbuluo.util.tool.JSONResult; /** * 用户控制器 * */ @Controller @RequestMapping("/user") public class UserController extends JSONResult{ /** * 用户 */ @Resource private IUserService userService; /** * 日志 */ private Logger log = LoggerFactory.getLogger(UserController.class); /** * 登陆 * @param userNm 用户名 * @param pwd 密码 * @return */ @ResponseBody @RequestMapping(value = "/login") public Map<String, Object> login(String userNm, String pwd){ /** * 获取subject */ Subject currentUser = SecurityUtils.getSubject(); if (!currentUser.isAuthenticated()) { // 把用户名和密码封装为 UsernamePasswordToken 对象 UsernamePasswordToken token = new UsernamePasswordToken(userNm, pwd); token.setRememberMe(true); try { // 执行登录. currentUser.login(token); } catch (UnknownAccountException e) { log.error("账号不存在: " + e.getMessage()); return super.result(RequestHttpCode.SC_NO_CONTENT); } catch (IncorrectCredentialsException e) { log.error("密码错误: " + e.getMessage()); return super.result(RequestHttpCode.SC_LOGIN_PASSWORD_ERROR); } catch (AuthenticationException e) { // 所有认证时异常的父类. log.error("登录失败: " + e.getMessage()); return super.result(RequestHttpCode.SC_BAD_REQUEST); } } return super.result(RequestHttpCode.SC_OK); } /** * 登出 * @return */ @RequestMapping(value = "logout") public String logout(){ SecurityUtils.getSubject().logout(); return InternalResourceViewResolver.REDIRECT_URL_PREFIX + "/"; } /** * 查询 * @return */ @ResponseBody @RequestMapping(value = "findById") public Map<String, Object> findById(){ try { userService.findById(); } catch (UnauthorizedException e){ System.out.println("权限不够"); } catch (Exception e) { // TODO: handle exception } return null; } }
这样的一个基本的shiro权限控制就好了 权限控制主要在
<property name="filterChainDefinitions"> <value> /jsp/login.jsp = anon /user/login = anon /user/logout = logout /jsp/admin.jsp = roles[user] /user/findById = roles[admin] /** = authc </value> </property>
配置页面的标签和注解控制页面显示还是隐藏。链接的访问程度
后续会继续深入了解shiro的,也会继续更新