1、简述
1、1 Apache Shiro是Java的一个安全框架。是一个相对简单的框架,主要功能有认证、授权、加密、会话管理、与Web集成、缓存等。
1、2 Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。
2、ssm集成shiro
2、1 pom.xml文件中添加依赖
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</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-core</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${shiro.version}</version> </dependency>
2、2 新建shiro配置文件spring-shiro.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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:annotation-config /> <context:component-scan base-package="com.hbst.basessm_1" /> <!-- proxy-target-class="true"强制使用CGLib代理,为false则spring会自动选择,否则事务不生效 --> <aop:aspectj-autoproxy proxy-target-class="true" /> <!-- 配置relam jdbc当调用验证时会调用此对象去查数据 --> <bean id="myShiroService" class="com.hbst.basessm_1.shiro.MyShiroService"></bean> <bean id="authzPathFilter" class="com.hbst.basessm_1.shiro.ShiroAuthzPathFilter"></bean> <!-- 配置权限管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="myShiroService" /> <property name="cacheManager" ref="cacheManager" /> </bean> <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 调用我们配置的权限管理器 --> <property name="securityManager" ref="securityManager" /> <!-- 配置我们的登录请求地址 --> <property name="loginUrl" value="/" /> <!-- 配置我们在登录页登录成功后的跳转地址,如果你访问的是非/login地址,则跳到您访问的地址 --> <property name="successUrl" value="/main" /> <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 --> <property name="unauthorizedUrl" value="/" /> <!-- 权限配置 --> <!-- /view/**=authc,perms[READS] --> <property name="filterChainDefinitions"> <value> /=anon /js/**=anon /theme/**=anon /main=authc /view/**=authc </value> </property> </bean> <!-- bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManager" ref="cacheManager"/> </bean --> <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" /> <!-- 配置shiro bean生命周期管理类 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> </beans>
2、3 在web.xml中,加入spring-shiro.xml,将shiro的配置加入项目的上下文环境中
2、4 在web.xml中,配置shiro安全过滤器
<!-- 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>
3、实现登录认证和授权
3、1 到这里,ssm和shiro的整合就完成了,下面,我们使用shiro完成登录认证,首先,先创建一张user表,并插入一条记录
为了简单,我这里使用明文密码,使用加密只需要在接收到前台提交的密码之后使用加密算法加密即可
3、2 创建UserController.java
package com.hbst.basessm_1.controller; import javax.annotation.Resource; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.hbst.basessm_1.pojo.request.LoginParamRequest; import com.hbst.basessm_1.service.IUserService; import com.hbst.basessm_1.util.baseUtil.ResourceUtils; import com.hbst.basessm_1.util.constant.CodeConstant; import com.hbst.basessm_1.util.entity.ResultMessage; import com.hbst.basessm_1.util.exception.BusinessException; import com.hbst.basessm_1.util.log.ISCLog; import com.hbst.basessm_1.util.log.impl.SCLogger; import net.sf.json.JSONObject; /** * @author Dean */ @Controller @RequestMapping("/user") public class UserController { private static final ISCLog logger = new SCLogger(UserController.class); @Resource private IUserService userService; /** * * @return */ @ResponseBody @RequestMapping(value = "/queryUser.do", method = RequestMethod.POST) public Object queryUser(LoginParamRequest request, BindingResult result) { logger.begin(); ResultMessage message = new ResultMessage(); try { // 进行登录验证 Subject subject = SecurityUtils.getSubject(); //获取登录的用户名密码 String userName = request.getUserName(); String password = request.getPassword(); //生成令牌,此处可以使用令牌来实现记住我的功能 UsernamePasswordToken token = new UsernamePasswordToken(userName, password); //token.setRememberMe(true); //认证, subject.login(token); //判断是否通过验证 if (subject.isAuthenticated()) { logger.debug("User Login Succeed", userName); message.setCode(CodeConstant.SUCCESS);// 成功的状态码 logger.end(); } } catch (BusinessException e) { JSONObject json = new JSONObject(); message.setCode(e.getErrorCode()); message.setCodeDesc(e.getErrorDes()); logger.error(e); } catch (Exception e) { message.setCode(CodeConstant.SYSTEM_ERROR); message.setCodeDesc(ResourceUtils.getResultCodeDesc(CodeConstant.SYSTEM_ERROR)); logger.error(e); } logger.end(); return message; } }
3、3 创建MyShiroService.java,这个java类是我们的realm,声明在spring-shiro.xml文件中,realm中有doGetAuthorizationInfo方法用于授权,doGetAuthenticationInfo方法用于登录认证
package com.hbst.basessm_1.shiro; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; 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.IncorrectCredentialsException; 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.realm.AuthorizingRealm; import org.apache.shiro.session.Session; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import com.hbst.basessm_1.service.IUserService; import com.hbst.basessm_1.util.log.impl.SCLogger; import net.sf.json.JSONObject; @Service @Scope("prototype") public class MyShiroService extends AuthorizingRealm { private static final SCLogger logger = new SCLogger(MyShiroService.class); private UserManage userManage = null; @Autowired private IUserService userService; /** * 授权方法 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { logger.begin(); try { // 获取当前登录的用户名,等价于(String)principals.fromRealm(this.getName()).iterator().next() String loginName = (String) super.getAvailablePrincipal(principalCollection); logger.debug("AuthorizationInfo Login Account", loginName); Map<String, Object> parameterMap = new HashMap<String, Object>(); parameterMap.put("userAccount", loginName); logger.debug("userAccount parameterMap", parameterMap); // 到数据库查是否有此用户 JSONObject userObject = userService.queryLoginUserAccount(parameterMap); userManage = (UserManage) JSONObject.toBean(userObject, UserManage.class); logger.debug("queryLoginUserAccount Result", userManage); if (null != userManage ) { if(userManage.getLockStatus()==0){ // 权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission) SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 获取用户角色列表 info.addRole(userManage.getUserType().toString()); logger.debug("Set User Roles", userManage.getUserType().toString()); // 获取权限列表 ArrayList<String> perListObject = userService.queryLoginUserPermission(parameterMap); logger.debug("queryLoginUserPermission Result", perListObject); info.addStringPermissions(perListObject); logger.debug("Get Module Permissions", perListObject.toString()); logger.end(); return info; }else{ } } logger.end(); } catch (Exception e) { logger.error(e.getMessage(), e); } return null; } /** * 登录认证方法 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException { logger.begin(); // UsernamePasswordToken对象用来存放提交的登录信息 UsernamePasswordToken token = (UsernamePasswordToken) arg0; logger.debug("Token User Name", token.getUsername()); try { Map<String, Object> parameterMap = new HashMap<String, Object>(); parameterMap.put("userName", token.getUsername()); parameterMap.put("userPassword", token.getPassword()); // 到数据库查是否有此对象 JSONObject userManageJsonObject = userService.queryUser(parameterMap); if (null == userManageJsonObject || userManageJsonObject.isEmpty()) { logger.error("INVALID USER ACCOUNT|" + token.getUsername()); throw new UnknownAccountException("用户名或者密码错误"); } userManage = (UserManage) JSONObject.toBean(userManageJsonObject, UserManage.class); if (null == userManage) { logger.error("INVALID USER ACCOUNT|" + token.getUsername()); throw new UnknownAccountException("用户名或者密码错误"); } String userName = userManage.getUserName().toLowerCase(); String userPassword = userManage.getPassword(); logger.debug("Query User Account", userName + "|" + userPassword); // 若存在,将此用户存放到登录认证info中 SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userName, userPassword, getName()); logger.debug("User Login Verify Succeed", userName); logger.end(); return info; } catch (UnknownAccountException e) { // 用户名未知 logger.error(e.getMessage(), e); throw e; } catch (IncorrectCredentialsException e) { // 凭据不正确,例如密码不正确 logger.error(e.getMessage(), e); throw e; } catch (AuthenticationException e) { // 其他未指定异常 logger.error(e.getMessage(), e); throw e; } catch (Exception e) { } logger.end(); return null; } }
3、4 创建接口IUserService.xml
package com.hbst.basessm_1.service; import net.sf.json.JSONObject; /** * */ public interface IUserService { /** * 查询用户 * @return */ public abstract JSONObject queryUser(Object jsonParameter) throws Exception; }
3、5 创建实现类UserService.java,这个类是真正用于与数据库交互,查询数据用的,下面的代码只实现了认证查询用户的方法,没有实现授权查询角色权限的方法
package com.hbst.basessm_1.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.hbst.basessm_1.dao.IBaseDao; import com.hbst.basessm_1.pojo.User; import com.hbst.basessm_1.service.IUserService; import com.hbst.basessm_1.util.baseUtil.StringUtil; import com.hbst.basessm_1.util.constant.CodeConstant; import com.hbst.basessm_1.util.constant.CommonConstant; import com.hbst.basessm_1.util.exception.BusinessException; import com.hbst.basessm_1.util.log.ISCLog; import com.hbst.basessm_1.util.log.impl.SCLogger; import net.sf.json.JSONObject; /** * 用户实现类 */ @Service("userService") @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public class UserServiceImpl implements IUserService{ private static final ISCLog logger = new SCLogger(UserServiceImpl.class); @Autowired private IBaseDao baseDao; /** * * 查询用户是否存在 * @return */ @Override public JSONObject queryUser(Object jsonParameter) throws Exception { logger.begin(); if (StringUtil.isEmpty(jsonParameter)) { throw new BusinessException(CodeConstant.PARAMS_ERROR); } logger.debug("Parameter is:", jsonParameter); //检查参数是否为空 // 检查参数是否存在且符合不允许为空的规则 JSONObject jsonObject = JSONObject.fromObject(jsonParameter); // 获取参数,确保参数的正确性 User user = (User) baseDao.findOneByCustom("selectUser", jsonObject); JSONObject resultJsonObject = JSONObject.fromObject(user); logger.debug("Object Conversion Json Result:", resultJsonObject.toString()); logger.end(); return resultJsonObject; } }
3、6 页面js
3、6、1 登录请求方法
// 异步提交表单 $.ajax({ url:"${pageContext.request.contextPath}/user/queryUser.do", dataType : "json", data: {'userName': name,'password':pwd}, cache: false, type: "POST", success : function(data){ if(data.code=="000000"){ window.location.replace("${pageContext.request.contextPath}/main.jsp"); }else{ if (undefined != data.codeDesc){ alert(data.codeDesc); }else{ alert("无效的登录用户名或密码!"); } } } });
3、6、2 授权页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%> <jsp:include page="../Main/include_head.jsp"></jsp:include> <%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> <!DOCTYPE html> <html> <head lang="en"> </head> <body> <section class="content-header"> <h1> <small>test</small> </h1> <ol class="breadcrumb"> <li><a href="#"><i class="fa fa-dashboard"></i>test</a></li> <shiro:hasPermission name="user:create"> <li><a href="#">test</a></li> </shiro:hasPermission> </ol> </section> <!-- Main content --> </body> </html>
3、7,流程说明
3、7、1:认证流程说明
登录认证,首先由前端页面发出请求,controller获取到前端提交的用户名和密码,生成令牌,然后调用subject.login(token)方法,此方法会先调用realm中的doGetAuthenticationInfo方法进行认证。认证成功跳转到配置文件中配置的跳转页面
3、7、2:授权流程说明
用户授权,会调用realm中的doGetAuthorizationInfo方法。调用此方法的方式有三种:
1、subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去调用这个是否有什么角色或者是否有什么权限的时候;
2、@RequiresRoles("admin") :在方法上加注解的时候;
3、[shiro:hasPermission name = "admin"] :在页面上加shiro标签的时候,即进这个页面的时候扫描到有这个标签的时候。如果在页面上使用shiro标签,
必须在头部加上<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>