shiro框架整合ssm
-
组成部分:Spring + SpringMVC + Mybatis + shiro;
-
本教程的使用环境为Java8, IDEA 以及 Maven;
-
开始教程
-
搭建SSM教程详见https://blog.csdn.net/weixin_51717204/article/details/119180393?spm=1001.2014.3001.5501;
-
在pom.xml文件中写入以下代码:
<!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.3.2</version> </dependency> <!-- 引入ehcache的依赖,给shiro做缓存权限用的 --> <!--<dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.10.6</version> </dependency>--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.12</version> </dependency>
-
在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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <context:component-scan base-package="cn.kgc"/> <context:property-placeholder location="classpath*:db.properties"/> <bean class="com.mchange.v2.c3p0.DriverManagerDataSource" id="dataSource"> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="driverClass" value="${jdbc.driver}"/> <property name="password" value="${jdbc.password}"/> <property name="user" value="${jdbc.username}"/> </bean> <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sessionFactory"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath*:/mappers/*Mapper.xml"/> </bean> <!--扫描dao层--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" id="configurer"> <property name="basePackage" value="cn.kgc.dao"/> </bean> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="dataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="testOnBorrow" value="true"/> <property name="maxTotal" value="100"/> <property name="maxIdle" value="8"/> </bean> <bean id="jedisPool" class="redis.clients.jedis.JedisPool"> <constructor-arg name="host" value="127.0.0.1"/> <constructor-arg name="port" value="6379"/> <constructor-arg name="poolConfig" ref="jedisPoolConfig"/> </bean> <!-- 配置 shiro 的核心组件:securityManager --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- 配置缓存 --> <property name="rememberMeManager" ref="rememberMeManager"/> <!-- 配置域realm,用户名,密码,角色都保存在域里:实现从数据库中获取用户信息,需要我们自己创建一个类(实现Realm接口) --> <property name="realm" ref="shiroRealm"/> </bean> <!-- 配置自己域realm实现 --> <bean id="shiroRealm" class="cn.kgc.service.shiro.UserRealm"> <property name="credentialsMatcher"> <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="MD5"></property> <property name="hashIterations" value="3"></property> </bean> </property> </bean> <!-- 配置shiro的一些拦截规则,id必须和web.xml中的 shiro 拦截器名一致 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- Shiro的核心安全接口,这个属性是必须的 --> <property name="securityManager" ref="securityManager"/> <!-- 身份认证失败,则跳转到登录页面的配置 --> <property name="loginUrl" value="/user/gotologin"/> <!-- 登录成功后的页面 --> <property name="successUrl" value="/admin/index"/> <!-- 权限认证失败,则跳转到指定页面 --> <property name="unauthorizedUrl" value="/unauthorized"/> <!-- 登录后访问没有权限的页面后跳转的页面 --> <!-- Shiro连接约束配置,即过滤链的定义 --> <property name="filterChainDefinitions"> <value> <!-- 注意:规则是有顺序的,从上到下,拦截范围必须是从小到大的 --> <!-- url = 拦截规则(anon为匿名,authc为要登录后,才能访问,logout登出过滤) --> /logins/login = anon /logout = anon /user/gotologin = anon /logins/admin = authc,permissionFilter,roles[admin] /user/** = authc </value> </property> <!-- 用户自定义的过滤器 --> <property name="filters"> <map> <entry key="permissionFilter" value-ref="userAccessControlFilter"/> </map> </property> </bean> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <!-- 会话Cookie模板 --> <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg value="sid"/> <property name="httpOnly" value="true"/> <!--maxAge=-1表示浏览器关闭时失效此Cookie --> <property name="maxAge" value="-1"/> </bean> <!-- rememberMeCookie:即记住我的Cookie,保存时长30天 --> <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg value="rememberMe"/> <property name="httpOnly" value="true"/> <property name="maxAge" value="2592000"/><!-- 30天 --> </bean> <!-- rememberMe管理器 --> <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager"> <property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64). decode('4AvVhmFLUs0KTA3Kprsdag==')}"/> <property name="cookie" ref="rememberMeCookie"/> </bean> </beans>
-
在applicationContext-Mvc.xml配置文件中写入以下代码:
<!-- 开启Shiro注解 --> <aop:config proxy-target-class="true"></aop:config> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean>
-
在web.xml中写入以下代码:
<!--spring 整合安全框架--> <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>
-
需要继承 AuthorizingRealm 类,并重写doGetAuthenticationInfo和doGetAuthorizationInfo方法
-
doGetAuthenticationInfo :该方法主要用于登录验证;
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken token1 = (UsernamePasswordToken) token; String username = token1.getUsername(); User user = userService.queryUserByUserCode(username); System.out.println(">>>>>" + user); if (user == null){ System.out.println(">>>>>user为null"); throw new UnknownAccountException("用户名或密码错误!"); } if (user .getStatus() == 0){ System.out.println("该用户已被禁用,请联系管理员!"); throw new UnknownAccountException("该用户已被禁用,请联系管理员!"); } SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUserCode(),user.getUserPassword(),getName()); authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(user.getSalt())); return authenticationInfo; }
-
doGetAuthorizationInfo :该方法主要用户权限认证;
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //获取当前user的用户名 String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal(); //构造一个授权凭证 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //通过你的用户名查询数据库,得到你的权限信息与角色信息,并存放到权限凭证中 info.addRole(getYourPermission(primaryPrincipal)); info.addStringPermissions(getYourPermissionByUsernameFromDB(primaryPrincipal)); final Set<String> stringPermissions = info.getStringPermissions(); System.out.println("这是权限信息:" + stringPermissions); //返回你的权限信息 return info; } public String getYourPermission(String username){ final User user = userService.queryUserByUserCode(username); if (user.getUserRole() == 1){ return "admin"; }else if (user.getUserRole() == 2){ return "manager"; }else if (user.getUserRole() == 3){ return "staff"; } return "error"; } public List<String> getYourPermissionByUsernameFromDB(String username){ final String yourPermission = getYourPermission(username); if (yourPermission.equals("admin")){ return Arrays.asList("code:insert","code:update","code:delete"); } return Arrays.asList("code:select"); }
-
-
权限认证需要自定义访问过滤器:
package cn.kgc.vo; import javafx.beans.binding.BooleanExpression; import org.apache.shiro.SecurityUtils; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; /** * @author hanyiming * @create 2021/9/2 13:16 */ @Component("userAccessControlFilter") public final class AccessControlFilter extends org.apache.shiro.web.filter.AccessControlFilter { private static final Logger LOGGER = LoggerFactory.getLogger(AccessControlFilter.class); /** * 是否允许访问,返回true表示允许 * 如果返回 false ,则进入本类的onAccessDenied方法中进行处理 * @param servletResponse * @param o * @return * @throws Exception */ @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse servletResponse, Object o) throws Exception { final Subject subject = SecurityUtils.getSubject(); //判断用户是否进行过登录认证,如果没有经过认证则返回登录页 if (subject.getPrincipal() == null || !subject.isAuthenticated()) { return Boolean.FALSE; } final String requestURI = this.getPathWithinApplication(request); if (LOGGER.isDebugEnabled()) { LOGGER.debug("请求URL为:{}", requestURI); } final String requestHeader = ((HttpServletRequest) request).getHeader("Referer"); //防盗链的处理 if (requestHeader == null || "".equals(requestHeader)) { return Boolean.FALSE; } //此处可以编写用于判断用户是否有相关权限的代码 //subject.hasRole("需要的角色"); //subject.isPermitted("需要的权限"); return Boolean.TRUE; } @Override protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { if (LOGGER.isDebugEnabled()) { LOGGER.debug("当前账号没有相应的权限!"); } //重定向到登录页面 this.redirectToLogin(servletRequest, servletResponse); return Boolean.FALSE; } }
-
注册方法代码为:
//生成盐值 ,存入数据库中 String salt = new SecureRandomNumberGenerator().nextBytes().toHex(); //将原始密码加盐,并且使用MD5算法加密一次,将最后结果存入数据库 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!注意此处加密方式与加密次数应与 applicationContext.xml配置文件中相同 String result = new Md5Hash(user.getUserPassword(),salt,1).toString(); user.setUserPassword(result); user.setSalt(salt); Integer integer = userMapper.addUser(user); log.info("<<<<<<<<<<<<<<<< 与用户添加完成,受影响行数:" + integer);
-