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";
}