1.需要的依赖
<dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>2.6.8</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-quartz</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.2</version> </dependency>
2.配置代理类
在web.xml中配置shiro与Spring集成需要的ShiroFilter代理类
<!-- shiro过虑器,DelegatingFilterProx会从spring容器中找shiroFilter --> <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> <welcome-file-list> <welcome-file>/index.jsp</welcome-file> </welcome-file-list>
3.创建spring-shiro配置文件
需要定义的bean为:
Realm
securityManager
shirofilter
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <!--配置自定义的realm--> <bean id="userRealm" class="cn.edu.neusoft.realm.UserRealm"> <property name="userService" ref="userServiceImpl"/> </bean> <!--配置安全管理器--> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="userRealm"/> </bean> <!--定义shiro过滤器--> <!-- 定义ShiroFilter --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/user/login"/> <property name="unauthorizedUrl" value="/nopermission.jsp"/> <property name="filterChainDefinitions"> <value> /user/test=anon /assets/**=anon /**=authc </value> </property> </bean> </beans>
需要注意:这里的shiroFilter和web.xml中配置的shiroFilter代理类名称必须一致,而且注意要将spring-shiro的配置文件在容器初始化的时候加载进来。
4.自定义Realm
需要继承AuthorizingRealm这个类并实现其中的方法,重写getName方法;
注入service时候使用的set方法进行注入,@setter为lombok插件省略getter setter;
package cn.edu.neusoft.realm; import cn.edu.neusoft.entity.User; import cn.edu.neusoft.service.UserService; import lombok.Setter; 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.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; /** * @author Chen * @create 2019-04-30 15:27 */ public class UserRealm extends AuthorizingRealm { @Setter private UserService userService; @Override public String getName() { return "UserRealm"; } /** * 授权 * * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } /** * 认证 * * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //从token中获取登录的用户名, 查询数据库返回用户信息 String username = (String) token.getPrincipal(); User user = userService.getUserByUsername(username); if (user == null) { return null; } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName()); return info; } }
5.Controller方法:
这里解释一下,使用了authc拦截器,当我们在spring-shiro.xml文件配置的登录地址和请求的登录地址一致时,有请求尽力啊,会判断用户是否登录了,如果登录的就放行,如果没有登录,这个拦截器会尝试获取请求中的帐号和密码,然后进行对比,如果对比正确,直接执行登录,反之,抛异常,返回到authc.loginUrl指定的路径。
@RequestMapping("login") public String login(Model model, HttpServletRequest req){ //如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名 String exceptionClassName = (String) req.getAttribute("shiroLoginFailure"); //根据shiro返回的异常类路径判断,抛出指定异常信息 if(exceptionClassName!=null){ if (UnknownAccountException.class.getName().equals(exceptionClassName)) { //最终会抛给异常处理器 model.addAttribute("errorMsg", "账号不存在"); } else if (IncorrectCredentialsException.class.getName().equals( exceptionClassName)) { model.addAttribute("errorMsg", "用户名/密码错误"); } else { //最终在异常处理器生成未知错误. model.addAttribute("errorMsg", "其他异常信息"); } } //此方法不处理登陆成功(认证成功),shiro认证成功会自动跳转到上一个请求路径 //登陆失败还到login页面 return "admin/login"; }