1.首先需要导入Shiro依赖包
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
2.定义自己的Reaml 需要继承AuthenticatingRealm 重写doGetAuthenticationInfo()方法;
AuthenticatingRealm 是用于完成用户身份验证的抽象类
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取用户身份令牌
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
//获取用户在页面中输入的账号
String username = token.getUsername();
/*
* 验证账号是否正确
* 实际工作中应该从数据库中获取数据
* 注意:
* 如果验证失败需要手动抛出异常AuthenticationException以及其子类异常
* AuthenticationException常用子类
* AccountException 用户账号异常
* UnknownAccountException 账号错误异常
* LockedAccountException 账户被冻结异常
* IncorrectCredentialsException 密码错误异常
*/
if (!"admin".equals(username)&&!"zhangsan".equals(username)){
throw new UnknownAccountException("账号输入错误");
}else if ("zhangsan".equals(username)){
throw new LockedAccountException("账号被冻结");
}
//数据库中的密码,
String dbPassword = "123456";
//返回验证密码的对象,Shiro会自定根据对象的数据用来验证密码是否正确
//如果验证是白Shiro会抛出一个异常信息IncorrectCredentialsException
return new SimpleAuthenticationInfo(username,dbPassword,this.getName());
3.写Shiro的配置类,将自定义的MyReaml,Shiro的安全管理器:SecurityManager,Shiro过滤器:ShiroFilterFactoryBean注册到Spring的上下文中
@Configuration
public class ShiroConfig {
//将自定义的realm类注入到容器中
@Bean
public Realm myRealm(){
return new MyRealm();
}
@Bean
public SecurityManager securityManager(Realm myRealm){
return new DefaultWebSecurityManager(myRealm);
}
//将shiro的过滤器定义到spring的应用上下文中去
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
//设置完全管理器
factoryBean.setSecurityManager(securityManager);
factoryBean.setLoginUrl("/");//设置如果用户没有登录后转向的页面
factoryBean.setSuccessUrl("/success");//设置登录成功后跳转的页面
factoryBean.setUnauthorizedUrl("/noperms");//设置没有权限后的转向地址
LinkedHashMap<String,String> chain = new LinkedHashMap();
//配置/logout 为登出请求,用户Shiro清空会话中的数据并自动返回未登录所转向的页面
chain.put("/logout", "logout");
//配置/login用户登录请求不需要登录即可访问
chain.put("/login", "anon");
//设置以admin开头的所有请求必须登录后才可以访问
chain.put("admin/**", "authc");
chain.put("user/**", "authc");
//设置所以请求必须要登录后才能访问,由于范围比较大因此一定要写在最后的位置
//注意:由于是拦截了所以请求都需要登录,这些请求也包括静态资源,例如js,css,图片
chain.put("/**", "authc");
factoryBean.setFilterChainDefinitionMap(chain);//
return factoryBean;
}
}
4.在Controller层中定义登录方法login
@RequestMapping("/login")
public String login(String username, String password, Model model){
//获取当前操作对象
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
/*
* 判断当前的操作用户是否完成了用户登录认证,(用户是否登录)进入if表示用户已经登录
* 如果写了这个if那么用户将无法重复登录,因为在Subject中只要用户登录成功就会将用户
* 信息记录到Subject中,因此isAuthenticated 这个方法返回true
* 如果需要用户能够再次登录有3中解决方案
* 1.不写这个if,但是会造成重复发送用户登录请求,增加服务器压力
* 2.调用Subject中的logout()方法,用于退出当前的登录状态,清空会话中的所用数据
* 3.在页面中编写一个安全退出请求,用于用户主动退出登录状态
*
*/
if (subject.isAuthenticated()){
return "redirect:/success";
}
//到了这里表示用户没有登录
//调用用户登录方法,后台Shiro会自动执行MyRealm中的用户认证方法doGetAuthenticationInfo
//如果登录成功login方法将正常结束否则将抛出异常,我们要更具异常信息对用户进行友好提示
try {
subject.login(token);
}catch (UnknownAccountException e){
model.addAttribute("errMsg", "登录失败,账号不存在");
return "index";
}catch (LockedAccountException e){
model.addAttribute("errMsg", "登录失败,账号以冻结");
return "index";
}catch (IncorrectCredentialsException e){
model.addAttribute("errMsg", "登录失败,密码错误");
return "index";
}catch (AuthenticationException e){
model.addAttribute("errMsg", "登录失败");
return "index";
}
//登录成功,不需要记录Session会话,Shiro会将这些用户状态信息记录到会话中(记录在Subject中)
return "redirect:/success";
}