一、用maven导入相关依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.2</version>
</dependency>
</dependencies>
二、先创建一个验证的MyShiroRealm类,继承AuthorizingRealm类,重写doGetAuthorizationInfo方法和doGetAuthenticationInfo方法
package springboot.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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 java.util.HashSet;
import java.util.Set;
public class MyShiroRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取用户
User user = (User)SecurityUtils.getSubject().getPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//获取用户角色
Set<String> roleSet = new HashSet<String>();
roleSet.add("100002");
info.setRoles(roleSet);
//获取用户权限
Set<String> permissionSet = new HashSet<String>();
permissionSet.add("sys:acc:aaaa");
permissionSet.add("权限删除");
info.setStringPermissions(permissionSet);
return info;
}
/**
* 验证用户身份,如果验证失败,返回null或者异常(带返回message),跳转到login的post链接
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
String username = token.getUsername();
if(!"aaa".equals(username)){
throw new AuthenticationException("用户不存在");
}
//没连接数据库,写死账号密码
User user = new User("aaa","123");
//进行认证,将正确数据给shiro处理
//密码不用自己比对,AuthenticationInfo认证信息对象,一个接口,new他的实现类对象SimpleAuthenticationInfo
AuthenticationInfo authcInfo=new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
//清之前的授权信息
super.clearCachedAuthorizationInfo(authcInfo.getPrincipals());
SecurityUtils.getSubject().getSession().setAttribute("login", user);
return authcInfo;//返回给安全管理器,securityManager,由securityManager比对数据库查询出的密码和页面提交的密码
}
}
注:doGetAuthorizationInfo方法只有在以下三种情况才会被调用,正常登录成功不进doGetAuthorizationInfo方法
(1)subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己调用这个是否有什么角色或者是否有什么权限的时候;
(2)@RequiresRoles("admin") :在方法上加注解的时候;
(3)[@shiro.hasPermission name = "admin"][/@shiro.hasPermission]:在页面上加shiro标签的时候,即进这个页面的时候扫描到有这个标签的时候。
三、创建ShiroConfiguration类配置shiro
package springboot.shiro;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Shiro 配置
*
* Apache Shiro 核心通过 Filter 来实现,就像SpringMvc 通过DispachServlet 来控制一样。
* Filter 是通过URL规则来进行过滤和权限校验,所以需要定义一系列关于URL的规则和访问权限。
*
*/
@Configuration
public class ShiroConfiguration {
/**
* ShiroFilterFactoryBean 处理拦截资源文件问题。
* 注:单独一个ShiroFilterFactoryBean配置是或报错的,因为在
* 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
*
Filter Chain定义说明
1、一个URL可以配置多个Filter,使用逗号分隔
2、当设置多个过滤器时,全部验证通过,才视为通过
3、部分过滤器可指定参数,如perms,roles
*
*/
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index");
// 未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
// 拦截器.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/verify", "anon");
// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/add", "perms[权限添加]");
// <!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
// <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
System.out.println("Shiro拦截器工厂类注入成功");
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm.
securityManager.setRealm(myShiroRealm());
return securityManager;
}
/**
* 身份认证realm; (这个需要自己写,账号密码校验;权限等)
*
*/
@Bean
public MyShiroRealm myShiroRealm() {
return new MyShiroRealm();
}
}
注:这里我登录页面访问的是login,form表单提交的是verify,所以需要打开verify的访问权限
@RequestMapping("/login")
public String login() {
return "login";
}
<form action="verify" method="post">
filterChainDefinitionMap.put("/verify", "anon");
四、controller代码
package springboot.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import springboot.shiro.User;
import javax.servlet.http.HttpServletRequest;
@Controller
public class JspController {
@RequestMapping("/verify")
public String userlogin(User user, RedirectAttributes attr, Model model) {
if(user.isEmpty()){return "login";}
String username=user.getUsername();
String password=user.getPassword();
UsernamePasswordToken token = new UsernamePasswordToken(username,password,false);
Subject currentUser = SecurityUtils.getSubject();
try {
//调用realm的认证方法
currentUser.login(token);
} catch(IncorrectCredentialsException e){
model.addAttribute("message", "密码错误");
return "login";
} catch (AuthenticationException e) {
attr.addFlashAttribute("test", "登录失败");
return"redirect:/login?message='登录失败'";
}
return "redirect:/index";
}
@RequestMapping(value="logout",method =RequestMethod.GET)
public String logout(HttpServletRequest request){
//subject的实现类DelegatingSubject的logout方法,将本subject对象的session清空了
//即使session托管给了redis ,redis有很多个浏览器的session
//只要调用退出方法,此subject的、此浏览器的session就没了
try {
//退出
SecurityUtils.getSubject().logout();
} catch (Exception e) {
System.err.println(e.getMessage());
}
return "login";
}
@RequestMapping(value="403")
public String unAuth(){
return "403";
}
@RequestMapping("/login")
public String login() {
return "login";
}
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
注:
(1)登录调用了logout方法,清空了subject对象的session
SecurityUtils.getSubject().logout();
(2)如果使用重定向的话,可以使用RedirectAttributes对象传参数,Jsp页面接收值比较特殊
attr.addFlashAttribute("test", "登录失败");
return"redirect:/login?message='登录失败'";
${sessionScope['org.springframework.web.servlet.support.SessionFlashMapManager.FLASH_MAPS'][0]['test']
(3)转发使用Model传参
model.addAttribute("message", "密码错误");
return "login";
${message}
五、JSP页面代码
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/layer/layer.js"></script>
</head>
<body style="text-align: center">
<form action="verify" method="post">
<p>账号:<input type="text" name="username" value="admin"/></p>
<p>密码:<input type="text" name="password" value="123456"/></p>
<P><input type="checkbox" name="rememberMe" />记住我</P>
<p><input type="submit" value="登录"/></p>
<br/>
<div>${sessionScope['org.springframework.web.servlet.support.SessionFlashMapManager.FLASH_MAPS'][0]['test']}${message}</div>
</form>
</body>
<script type="text/javascript">
</script>
</html>