用 shiro 实现最基本的登录认证是一件非常简单的事情,我觉得官网给的例子不太好用,反正我是没搞出来,现在写一份自己的教程。
项目地址:https://github.com/thecattle/spring-mvc-shiro
目的
- 集成 shiro 到 spring 上
- 实现最基本的登录认证
项目环境
- spring+spring-mvc
- maven3
- IntelliJ IDEA
- jdk1.8
1. 添加 shiro 配置
1.1 修改 pom 文件
添加 shiro maven :
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
1.2 修改 web.xml 文件
<!--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>
1.3 为了项目结构清晰,单独新建 shiro 配置文件
在 resource 目录新建 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm">
<!--自定义 MyRealm,登录的认证入口 ,需要继承AuthorizingRealm,项目中会体现-->
<bean class="com.sunp.shiro.MyRealm"></bean>
</property>
</bean>
<!--shiro 请求拦截器,这里的 bean id 一定要对应 web.xml 中的filter-name,否则找不到这个拦截器-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"></property>
</bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 ,不明觉厉,没有深究-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
</beans>
1.4 修改 spring 配置文件 applicationContext.xml
将 shiro.xml导入到 spring 容器中
<?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"
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">
<!--启用spring的一些annotation -->
<context:annotation-config/>
<!--在 shiro 中用到了 UserService,shiro 是运行在 spring 容器中的, shiro 和 spring-mvc 级别是平行的,
所以 spring-mvc 的扫描不起作用,这里再扫描一次,所有的 bean 加载到 spring 容器中-->
<context:component-scan base-package="com.sunp.**"></context:component-scan>
<!--导入 shiro 配置-->
<import resource="classpath:shiro.xml"></import>
</beans>
配置到这里就差不多了。
2. 添加 shiro 项目代码
2.1 新建自定义的 realm
我这里创建在 com.sunp.shiro包下,对应 shiro.xml 中的配置。仔细看注释
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/**
* 认证用户的操作
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
//从假数据源中拿当前用户数据
User userDao = userService.getUserInfo(token.getUsername());
if (userDao==null){
//扔出个异常,currentUser.login(token);可以 catch 到
throw new UnknownAccountException("未知用户");
}
//如果数据库中存在这个用户名
if(userDao.getUserName().equals(token.getUsername())){
//创建认证对象,传入数据库存在的对象账号和密码,内部会根据 currentUser.login(token) 的 token 进行比较
AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(userDao.getUserName(), userDao.getUserPassword(), this.getName());
return authcInfo;
}
return null;
}
}
2.2 在 LoginController 中添加登录方法
//方便调试 直接用 get
@RequestMapping(value = "/login",method = RequestMethod.GET)
@ResponseBody
public Map<String,Object> login(String username,String password){
Map<String,Object> map=new HashMap<>();
logger.info("开始登录");
//构建登录 token
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//设置记住我
token.setRememberMe(true);
//获取当前登录用户
Subject currentUser = SecurityUtils.getSubject();
try {
//登录
currentUser.login(token);
//判断用户状态是否已经被认证
if (currentUser.isAuthenticated()){
map.put("msg","登录成功");
}else {
map.put("msg","系统异常,请重试");
}
} catch (UnknownAccountException uae) {
map.put("msg","未知用户");
} catch (IncorrectCredentialsException ice) {
map.put("msg","认证失败");
} catch (LockedAccountException lae) {
map.put("msg","账户已锁定");
} catch (ExcessiveAttemptsException eae) {
map.put("msg","登录失败次数过多");
} catch (AuthenticationException ae) {
map.put("msg",ae.getMessage());
}
return map;
}
3. 项目结构
最后的项目结构如下:蓝色为修改过文件,绿色为新增文件
4. 测试
4.1 假的数据源为:
4.2 测试正确
4.3 测试不存在的用户
4.4 测试错误的密码
自学笔记,如有错误,期待评论指正