Shiro+SSM整合,认证授权 小灰灰 2022-05-24 02:22 356阅读 0赞 ** 环境 : ** Shiro+SSM整合基于你已经拥有一个可运行的SSM+Maven环境。 ** 主体 :** ** ** 1. 导入shiro相关jar包 2. web.xml配置shiro的代理过滤器,shiro的入口,相当于建立起servlet与shiro的联系 3. 配置shiro的xml文件 4. 自定义 Realm,获取数据库安全数据 5. controller层调用shiro的login方法验证用户信息 ** 步骤 :** ** 1.** pom.xml导入相关jar包 <!-- Shiro的jar包 Start --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.1</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.1</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.1</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.1</version> </dependency> <!-- Shiro的jar包 End --> 2. 配置web.xml 这个地方只需要在web.xml中添加如下配置 <!-- 如果同一个bean被定义两次,后面一个优先 --> <!-- classpath*与classpath的区别:前者遍历所有的classpath加载所有符合条件的资源(效率较差),后者只会去找第一个符合条件的资源(避免重名) --> <context-param> <description></description> <param-name>contextConfigLocation</param-name> <param-value> classpath:spring-mybatis.xml, classpath:spring-shiro.xml </param-value> </context-param> <!-- shiro过滤器, DelegatingFilterProxy代理会自动的到IOC容器找名字为shiroFilter的bean; 如果要修改bean的名字,可以设置targetBeanName的值,要注意的是必须与ShiroFilterFactoryBean的id值保持一致 --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> filter-name 默认是shiroFilter,要修改的话可以在这个地方配置,为了改个名字添加额外配置,个人感觉这个很鸡肋,不做介绍。 3. 定制自己的shiro服务,我这里命名spring-shiro.xml 要注意的是文件名和web.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" default-lazy-init="true"> <!-- 配置安全管理器securityManager, 缓存技术: 缓存管理 realm:负责获取处理数据 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="myShiroRealm" /> <property name="cacheManager" ref="cacheManager" /> </bean> <!-- 項目自定义的Realm,从数据库中获取用户的安全数据 --> <bean id="myShiroRealm" class="com.hans.shiro.UserRealm"> <!-- 配置缓存管理器--> <property name="cacheManager" ref="cacheManager" /> <!-- 配置加密器 --> <property name="credentialsMatcher"> <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="MD5"></property> <!-- 加密算法的名称 --> <property name="hashIterations" value="1024"></property> <!-- 配置加密的次数 --> </bean> </property> </bean> <!-- 用户授权信息Cache --> <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" /> <!-- 必须配置lifecycleBeanPostProcessor:管理shiro中常见的对象 --> <!-- 保证实现了Shiro内部lifecycle函数的bean执行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- shiro的核心配置: 配置shiroFileter id名必须与web.xml中的filtername保持一致 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <property name="loginUrl" value="/logon.jsp" /> <property name="successUrl" value="/index.jsp" /> <property name="unauthorizedUrl" value="/unauthorized.jsp" /> <!-- shiro过滤器的具体配置 --> <!-- anon-匿名访问,authc-需要认证 --> <property name="filterChainDefinitions"> <value> /logon.jsp = anon /user/logon = anon /index.jsp = authc /admin/** = roles[admin] /user/** = roles[user] /logout=logout /** = authc </value> </property> </bean> </beans> 配置中介绍的比较详细,不涉及的知识点有多Realm配置及认证策略,以上的配置属于单个Realm的AtLeastOneSuccessfulStrategy认证策略(有一个成功就登录成功)。 还有URL匹配优先级问题,简单描述就是从上到下依次匹配,以第一次匹配到的为准。这个很好理解,举个例子,/logon.jsp = anon 这个表达式的意思是根目录下的logon.jsp可以匿名访问,/\*\* = authc 这个表达式表示所有的资源都需要认证过后才能访问,很明显/\*\*包括/logon.jsp,而实际上我们访问logon.jsp是可以匿名访问的,原因就是URL匹配优先级以第一次匹配到的规则为准。 4. 自定义 Realm package com.hans.shiro; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.LockedAccountException; 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.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import com.hans.entity.User; import com.hans.service.UserService; /** * @todo 自定义 Realm,查询数据库并返回正确的数据 * @author Hans * @time 2018下午6:11:20 * */ public class UserRealm extends AuthorizingRealm{ @Autowired private UserService userSer; @Autowired private User us; /** * @see 授权,在配有缓存的情况下,只加载一次。 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) { //当前登录用户,账号 String userCode = principal.toString(); System.out.println("当前登录用户:"+userCode); //获取角色信息 List<Map<String, Object>> roleList = new ArrayList<Map<String, Object>>(); roleList = userSer.findRoles(userCode); Set<String> roles = new HashSet<String>(); if(roleList.size()>0){ for(Map<String, Object> role : roleList){ roles.add(String.valueOf(role.get("rcode"))); } }else{ System.out.println("当前用户没有角色!"); } SimpleAuthorizationInfo info = null; info = new SimpleAuthorizationInfo(roles); return info; } /** * @see 认证登录,查询数据库,如果该用户名正确,得到正确的数据,并返回正确的数据 * AuthenticationInfo的实现类SimpleAuthenticationInfo保存正确的用户信息 * */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //1.将token转换为UsernamePasswordToken UsernamePasswordToken userToken = (UsernamePasswordToken)token; //2.获取token中的登录账户 String userCode = userToken.getUsername(); //3.查询数据库,是否存在指定的用户名和密码的用户(主键/账户/密码/账户状态/盐) us = null; us = userSer.findUserByUserCode(userCode); //4.1 如果没有查询到,抛出异常 if( us == null ) { throw new UnknownAccountException("账户"+userCode+"不存在!"); } if( us.getStatus() == 0){ throw new LockedAccountException(us.getUsercode()+"被锁定!"); } //4.2 如果查询到了,封装查询结果, Object principal = us.getUsercode(); Object credentials = us.getPassword(); String realmName = this.getName(); String salt = us.getSalt(); //获取盐,用于对密码在加密算法(MD5)的基础上二次加密ֵ ByteSource byteSalt = ByteSource.Util.bytes(salt); SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, byteSalt, realmName); //5. 返回给调用login(token)方法 return info; } } 5. controller层验证用户信息 package com.hans.controller; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.AuthenticationException; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import com.hans.entity.User; import com.hans.service.UserService; /** * @todo * @author Hans * @time 2018下午6:34:04 * */ @Controller @RequestMapping("user") public class UserController { @Autowired UserService userSer; @Autowired User user; /** * @todo 用户登录 * @since 获取当前用户, * 判断用户是否已经认证登录, * 用账号密码创建UsernamePasswordToken, * 调用subject的login方法 * @param * @return */ @RequestMapping(method = RequestMethod.POST,value = "logon") public String logon(@RequestParam("userCode")String userCode,@RequestParam("password")String password){ //创建Subject实例对象 Subject currentUser = SecurityUtils.getSubject(); //判断当前用户是否已登录 if(currentUser.isAuthenticated() == false){ UsernamePasswordToken token = new UsernamePasswordToken(userCode,password); try { currentUser.login(token); } catch (AuthenticationException e) { e.getMessage(); e.printStackTrace(); System.out.println("登录失败"); return "logon"; } } return "index"; } } 配置完成,到这里就可以实现简单的登录授权功能。 下面看看效果,用户登录。 ![70][] ![70 1][] 点击管理员资源,跳转拦截 ![70 2][] 点击用户资源,跳转 ![70 3][] [70]: /images/20220524/02af5f39c3c140aaa0aef2e0239f8971.png [70 1]: /images/20220524/e1c505d2c44d4e19840940ededa01d9a.png [70 2]: /images/20220524/d8f92e87eec34a1ba021413a0c690ebe.png [70 3]: /images/20220524/83c8bc545cd640939c582b38a14d8bc4.png
还没有评论,来说两句吧...