之前一直使用spring security来做安全管理,感觉配置稍微有点复杂,于是尝试了下shiro,感觉的确简单不少。记录下配置和实现过程。
因为还是spring的底子,所以用的shiro-spring,首先用maven把相关包弄下来
dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency>
我用的版本是1.2.2
然后开始增加shiro的配置文件,xml里增加了shiro的配置引入,同时增加相应的filter
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:/application-root.xml, classpath*:/application-shiro.xml </param-value> </context-param> <!-- shiro security filter --> <filter> <!-- 这里的filter-name要和spring的applicationContext-shiro.xml里的 org.apache.shiro.spring.web.ShiroFilterFactoryBean的bean name相同 --> <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>
下面是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.2.xsd" default-lazy-init="true"> <description>Shiro安全配置</description> <bean id="chainDefinitionSectionMetaSource" class="xxx.xxx.ChainDefinitionSectionMetaSource"> <property name="filterChainDefinitions"> <value> /login = authc /logout = logout /admin/** = roles[admin] /static/** = anon /eventSurvey/** = anon /notice/** = anon /** = authc </value> </property> </bean> <!-- Shiro's main business-tier object for web-enabled applications --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="shiroDbRealm" /> </bean> <!-- 項目自定义的Realm, 所有accountService依赖的dao都需要用depends-on声明 --> <bean id="shiroDbRealm" class="xxx.xxx.ShiroDbRealm"> <property name="credentialsMatcher" ref="flameCredentialsMatcher" /> </bean> <bean id="flameCredentialsMatcher" class="xxx.xxx.FlameCredentialsMatcher"> </bean> <!-- Shiro Filter --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <property name="loginUrl" value="/login" /> <property name="successUrl" value="/" /> <property name="unauthorizedUrl" value="/authError" /> <property name="filters"> <map> <entry key="authc"> <bean class="xxx.xxx.MyFormAuthenticationFilter"></bean> </entry> <entry key="roles"> <bean class="org.apache.shiro.web.filter.authz.RolesAuthorizationFilter"></bean> </entry> </map> </property> <property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource" /> </bean> <!-- 保证实现了Shiro内部lifecycle函数的bean执行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> </beans>
这里需要说明几个配置
1)shiro本身的过滤配置已经很好了,但我们要求可从数据库加载过滤配置,所以增加了chainDefinitionSectionMetaSource的实现,用来同时从配置文件和数据库加载过滤信息,实现如下
public class ChainDefinitionSectionMetaSource implements FactoryBean<Ini.Section>{ @Autowired private ResourceDao resourceDao; private String filterChainDefinitions; public Section getObject() throws BeansException { //获取所有Resource List<Resource> list = resourceDao.findAll(); Ini ini = new Ini(); //加载默认的url ini.load(filterChainDefinitions); Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME); //循环Resource的url,逐个添加到section中。section就是filterChainDefinitionMap, //里面的键就是链接URL,值就是存在什么条件才能访问该链接 for (Resource resource : list) { //如果不为空值添加到section中 if(StringUtils.hasText(resource.getUrl()) && StringUtils.hasText(resource.getPerms())) { section.put(resource.getUrl(), resource.getPerms()); } } return section; } /** * 通过filterChainDefinitions对默认的url过滤定义 * * @param filterChainDefinitions 默认的url过滤定义 */ public void setFilterChainDefinitions(String filterChainDefinitions) { this.filterChainDefinitions = filterChainDefinitions; } public Class<?> getObjectType() { return this.getClass(); } public boolean isSingleton() { return false; } }
这里的Resource是从数据库得到的数据,其他跟xml配置差不多,就是key-value一样的。
2)shiroDbRealm,负责根据自己的业务抓取用户信息,主要是为了安全认证用
public class ShiroDbRealm extends AuthorizingRealm{ @Autowired private UserService userService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { UserInfo user = (UserInfo) principals.getPrimaryPrincipal(); List<String> userAuths = user.getAuthList(); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addRoles(userAuths); return info; } @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authcToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authcToken; UserInfo user = userService.findUserById(token.getUsername()); if (user != null) { String authPassword = user.getPassword(); return new SimpleAuthenticationInfo(user, authPassword,getName()); } else { return null; } } }
3)credentialsMatcher,这里是处理密码匹配用的,通常密码都是加密的,用户前台输入的密码需要自己加密算法来匹配
/** * 处理密码加密 * @author lee * */ public class FlameCredentialsMatcher extends SimpleCredentialsMatcher{ @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { String userId = token.getPrincipal().toString(); char[] ps = (char[]) token.getCredentials(); StringBuffer sb = new StringBuffer(); for(char p : ps){ sb.append(p); } String tokenMd5Pw = Encryption.encrypt(userId,sb.toString()); return equals(tokenMd5Pw, info.getCredentials()); } }
上面的Encryption.encrypt(userId,sb.toString())是一个加密算法处理,这个可以使用shiro的加密API,也可以自己实现。info是shiroDbRealm取回的用户信息,token是前台传过来的信息。
4)对于shiro的过滤链我使用了两个简单的,一个是处理登录和权限校验的authc,一个是处理角色校验的roles,这里需要说下的是如果在filter中不增加roles,那么配置/admin/** = roles[admin]这种就不会生效。对于authc因为要做是否是ajax的验证,所以做了自己的实现封装
public class MyFormAuthenticationFilter extends FormAuthenticationFilter{ private static final Logger log = LoggerFactory.getLogger(MyFormAuthenticationFilter.class); /* * 主要是针对登入成功的处理方法。对于请求头是AJAX的之间返回JSON字符串。 */ @Override protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { if (!isAjax(request)) {// 不是ajax请求 issueSuccessRedirect(request, response); } else { response.setCharacterEncoding("UTF-8"); response.setContentType("text/plain;charset=utf-8"); PrintWriter out = response.getWriter(); out.println("{\"success\":true,\"message\":\"登入成功\"}"); out.flush(); out.close(); } return false; } /** * 主要是处理登入失败的方法 */ @Override protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { if (!isAjax(request)) {// 不是ajax请求 setFailureAttribute(request, e); return true; } try { response.setCharacterEncoding("UTF-8"); response.setContentType("text/plain;charset=utf-8"); PrintWriter out = response.getWriter(); String message = e.getClass().getSimpleName(); if ("IncorrectCredentialsException".equals(message)) { out.println("{\"success\":false,\"message\":\"密码错误\"}"); } else if ("UnknownAccountException".equals(message)) { out.println("{\"success\":false,\"message\":\"账号不存在\"}"); } else if ("LockedAccountException".equals(message)) { out.println("{\"success\":false,\"message\":\"账号被锁定\"}"); } else { out.println("{\"success\":false,message:\"未知错误\"}"); } out.flush(); out.close(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } return false; } /** * 所有请求都会经过的方法。 */ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { log.info("权限验证"); if (isLoginRequest(request, response)) { if (isLoginSubmission(request, response)) { if (log.isTraceEnabled()) { log.trace("Login submission detected. Attempting to execute login."); } return executeLogin(request, response); } else { if (log.isTraceEnabled()) { log.trace("Login page view."); } //allow them to see the login page ;) return true; } } else { if (log.isTraceEnabled()) { log.trace("Attempting to access a path which requires authentication. Forwarding to the " + "Authentication url [" + getLoginUrl() + "]"); } if (!isAjax(request)) {// 不是ajax请求 saveRequestAndRedirectToLogin(request, response); } else { response.setCharacterEncoding("UTF-8"); response.setContentType("text/plain;charset=utf-8"); PrintWriter out = response.getWriter(); out.println("{\"success\":false,\"message\":\"login\"}"); out.flush(); out.close(); } return false; } } private boolean isAjax(ServletRequest request){ HttpServletRequest httpServletRequest = (HttpServletRequest) request; return "XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With")); } }
都配置完成,shiro就跑起来了
相关推荐
通过集成Spring,我们可以使用Shiro的SessionManager和SessionDAO,配合Spring Session,实现分布式会话存储。 8. **登录与登出** Shiro 提供了简单的登录和登出API。在Controller中,我们可以调用`Subject.login...
Spring 框架如何与 Shiro 配合: 1. **集成方式**:可以通过 Spring 的 Bean 定义来配置 Shiro,或者使用 Spring Boot 的自动配置特性。 2. **依赖注入**:通过 Spring 的 DI 功能,可以将 Shiro 组件注入到 Spring ...
- Shiro是一个强大的Java安全框架,提供认证、授权、加密和会话管理功能,可与Spring及其他框架无缝集成。 - 认证:处理用户身份验证,确认用户身份是否合法。 - 授权:控制已认证的用户能访问哪些资源。 - ...
spring springmvc mybatis shiro 以及 ehcache(配合shiro实现缓存验证 配合spring实现二级缓存) 测试 二级缓存 访问http://localhost:8080/vkblog/test/ehcacheuserlist.action 测试 访问限制 访问任意的action
在本 demo 中,你将看到如何配置 Shiro 与 Spring Boot 结合,包括设置 Realm、Filter 链、SessionManager 和 CacheManager。Shiro 的 Filter 链定义了请求处理的顺序,例如 AuthenticatingFilter 用于处理认证,...
6. **测试与调试**:配合Spring的测试框架,可以编写单元测试和集成测试,确保Shiro的权限设置正确无误。 压缩包中的文件可能包含以下内容: - `spring+springmvc+shiro.doc`:这可能是详细的集成指南或者示例代码...
在"spring mvc+shiro + jpa"的权限管理示例中,这三者通常会这样配合: 1. **用户认证**:Shiro 负责用户登录验证,通过用户名和密码与数据库交互,确认用户身份。Spring MVC 提供控制器接收登录请求并调用Shiro的...
Spring还允许开发者进行事务管理,提供数据访问抽象,并与其他Spring模块如Spring Security(原Acegi)配合,以增强应用的安全性。 3. **Struts2**:Struts2是一个基于MVC(Model-View-Controller)设计模式的Java ...
- **Web集成**:Shiro可以与Spring MVC无缝配合,处理HTTP请求的安全过滤。 3. **Shiro核心组件**: - **Subject**:代表当前“安全主体”,即操作系统的用户。 - **Realm**:是Shiro与应用程序安全数据源(如...
在Spring MVC项目中,Shiro可以与Spring结合,提供安全控制,例如登录验证、权限控制等功能。通过Shiro,开发者可以方便地定义角色和权限,实现对用户访问资源的精细控制。 Spring 4.x 是Spring框架的一个版本,它...
此外,Shiro还可以配合Spring的缓存抽象,如Spring Cache,进一步优化缓存管理。 实现这些功能涉及的步骤可能包括以下几点: 1. 配置Shiro:在Spring MVC的配置文件中,我们需要定义Shiro的...
配合Spring Boot的数据访问层,我们可以方便地从数据库中查询用户的角色和权限,并将这些信息映射到Shiro的权限模型中。 **3. Session共享** 在分布式系统中,Session共享是个挑战。Shiro提供了SessionManager和...
在`pom.xml`中引入Apache Shiro的相关组件,包括`shiro-spring`、`shiro-ehcache`以及`shiro-cas`,这些依赖是Shiro与Spring整合以及实现CAS单点登录的基础。确保已经正确安装了CAS服务器,这里用的是版本3.5.2。 ...
循序渐进,学习Spring Boot、Spring Boot & Shiro、Spring Cloud、Spring Security & Spring Security OAuth2,博客Spring系列源码 一、Spring Boot教程 开启Spring Boot Spring Boot基础配置 Spring Boot中使用...
总的来说,MyBatisPlus、Spring Boot和Shiro Plus的整合是现代Web开发中常见的一种架构模式,它们相互配合,为开发者提供了高效、便捷、安全的开发环境。通过深入理解并掌握这三者的源码,开发者可以更好地优化代码...
综上所述,这个项目是一个基于Spring Boot和Spring Cloud的分布式系统,使用Apache Shiro进行权限控制,通过Eureka实现服务注册与发现。数据库层面采用MySQL,用户信息和权限存储在数据库中,会话管理可能借助Redis...
Shiro是Java的一个安全框架,提供了会话管理、权限控制、身份认证等特性,与Spring Boot结合可以轻松实现权限控制。在Spring Boot中集成Shiro,我们可以通过编写自定义的Realm,对接JWT的验证逻辑,使得Shiro能够...
在整合这些框架时,开发者通常会创建一个Shiro Realm来处理认证和授权,这个Realm可以与Spring的DataSource配合,从数据库中获取用户信息。同时,利用Spring的AOP特性,可以方便地在全局范围内处理Shiro的拦截器,...
在本主题中,我们将深入探讨如何使用Spring Boot与Apache Shiro进行权限管理。Spring Boot以其简洁的配置和快速的应用开发特性,已经成为了Java领域中的热门框架。而Shiro则是一款强大的安全框架,提供了身份验证、...
标签中提到的SSO,可能表示示例展示了如何使用Shiro与其他SSO服务配合使用。 4. **Spring Security比较**:虽然Shiro和Spring Security都是Java安全框架,但它们各有特点。Shiro更轻量级,易于理解和使用,适合小型...