Spring Boot Shiro 权限管理
Posted catoop
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot Shiro 权限管理相关的知识,希望对你有一定的参考价值。
本来是打算接着写关于数据库方面,集成MyBatis的,刚好赶上朋友问到Shiro权限管理,就先总结下发出来了。
使用Shiro之前用在Spring MVC中,是通过XML文件进行配置。
既然现在在写Spring Boot的帖子,就将Shiro应用到Spring Boot中,我本地已经完成了SpringBoot使用Shiro的实例,将配置方法共享一下。
先简单介绍一下Shiro,对于没有用过Shiro的朋友,也算是做个简介吧。
Shiro是Apache下的一个开源项目,我们称之为Apache Shiro。它是一个很易用与Java项目的的安全框架,提供了认证、授权、加密、会话管理,与 Spring Security 一样都是做一个权限的安全框架,但是与Spring Security 相比,在于 Shiro 使用了比较简单易懂易于使用的授权方式。
Apache Shiro 的三大核心组件
- Subject 当前用户操作
- SecurityManager 用于管理所有的Subject
- Realms 用于进行权限信息的验证,也是我们需要自己实现的。
我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
Apache Shiro 核心通过 Filter 来实现,就好像SpringMvc 通过DispachServlet 来主控制一样。
既然是使用 Filter 一般也就能猜到,是通过URL规则来进行过滤和权限校验,所以我们需要定义一系列关于URL的规则和访问权限。
另外我们可以通过Shiro 提供的会话管理来获取Session中的信息。Shiro 也提供了缓存支持,使用 CacheManager 来管理。
官方网站:http://shiro.apache.org/
完整架构图:
下面我们通过代码实战来看下Spring Boot 中应用Shiro:
1、创建数据库表
表(t_permission)
id permissionname role_id
------ -------------- ---------
1 add 2
2 del 1
3 update 2
4 query 3
5 user:query 1
6 user:edit 2
表(t_role)
id rolename
------ ----------
1 admin
2 manager
3 normal
表(t_user)
id username password
------ -------- ----------
1 tom 123456
2 jack 123456
3 rose 123456
表(t_user_role)
user_id role_id
------- ---------
1 1
1 3
2 2
2 3
3 3
看截图,上面3张表是我测试别的用的,可以忽略。
下面是,数据库脚本和测试数据。
/*
SQLyog Ultimate v10.00 Beta1
mysql - 5.5.28 : Database - test
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
/*!40101 SET SQL_MODE=''*/;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`test` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `test`;
/*Table structure for table `t_permission` */
DROP TABLE IF EXISTS `t_permission`;
CREATE TABLE `t_permission` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`permissionname` varchar(32) DEFAULT NULL,
`role_id` int(11) DEFAULT NULL,
KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
/*Data for the table `t_permission` */
insert into `t_permission`(`id`,`permissionname`,`role_id`) values (1,'add',2),(2,'del',1),(3,'update',2),(4,'query',3),(5,'user:query',1),(6,'user:edit',2);
/*Table structure for table `t_role` */
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`rolename` varchar(32) DEFAULT NULL,
KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
/*Data for the table `t_role` */
insert into `t_role`(`id`,`rolename`) values (1,'admin'),(2,'manager'),(3,'normal');
/*Table structure for table `t_user` */
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) DEFAULT NULL,
`password` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
/*Data for the table `t_user` */
insert into `t_user`(`id`,`username`,`password`) values (1,'tom','123456'),(2,'jack','123456'),(3,'rose','123456');
/*Table structure for table `t_user_role` */
DROP TABLE IF EXISTS `t_user_role`;
CREATE TABLE `t_user_role` (
`user_id` int(11) DEFAULT NULL,
`role_id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `t_user_role` */
insert into `t_user_role`(`user_id`,`role_id`) values (1,1),(1,3),(2,2),(2,3),(3,3);
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
2、创建对应实体类
User.java
package org.springboot.sample.entity;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.hibernate.validator.constraints.NotEmpty;
/**
* 用户
*
* @author 单红宇(365384722)
* @myblog http://blog.csdn.net/catoop/
* @create 2016年1月13日
*/
@Entity
@Table(name = "t_user")
public class User
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@NotEmpty(message = "用户名不能为空")
private String username;
@NotEmpty(message = "密码不能为空")
private String password;
@ManyToMany(fetch=FetchType.EAGER)
@JoinTable(name = "t_user_role", joinColumns = @JoinColumn(name = "user_id") , inverseJoinColumns =
@JoinColumn(name = "role_id") )
private List<Role> roleList;// 一个用户具有多个角色
public User()
super();
public User(String username, String password)
super();
this.username = username;
this.password = password;
// 省略 get set 方法
@Transient
public Set<String> getRolesName()
List<Role> roles = getRoleList();
Set<String> set = new HashSet<String>();
for (Role role : roles)
set.add(role.getRolename());
return set;
Role.java
package org.springboot.sample.entity;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
/**
* 角色(管理员,普通用户等)
*
* @author 单红宇(365384722)
* @myblog http://blog.csdn.net/catoop/
* @create 2016年1月13日
*/
@Entity
@Table(name = "t_role")
public class Role
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String rolename;
@OneToMany(mappedBy = "role", fetch=FetchType.EAGER)
private List<Permission> permissionList;// 一个角色对应多个权限
@ManyToMany
@JoinTable(name = "t_user_role", joinColumns = @JoinColumn(name = "role_id") , inverseJoinColumns =
@JoinColumn(name = "user_id") )
private List<User> userList;// 一个角色对应多个用户
// 省略 get set 方法
@Transient
public List<String> getPermissionsName()
List<String> list = new ArrayList<String>();
List<Permission> perlist = getPermissionList();
for (Permission per : perlist)
list.add(per.getPermissionname());
return list;
Permission.java
package org.springboot.sample.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
/**
* 权限(增删改查等)
*
* @author 单红宇(365384722)
* @myblog http://blog.csdn.net/catoop/
* @create 2016年1月13日
*/
@Entity
@Table(name = "t_permission")
public class Permission
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String permissionname;
@ManyToOne
@JoinColumn(name = "role_id")
private Role role;// 一个权限对应一个角色
// 省略 get set
3、Shiro 配置,相当于SpringMVC 中的XML配置
ShiroConfiguration.java
package org.springboot.sample.config;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springboot.sample.dao.IScoreDao;
import org.springboot.sample.security.MyShiroRealm;
import org.springboot.sample.service.StudentService;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
/**
* Shiro 配置
*
* @author 单红宇(365384722)
* @myblog http://blog.csdn.net/catoop/
* @create 2016年1月13日
*/
@Configuration
public class ShiroConfiguration
private static final Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);
@Bean
public EhCacheManager getEhCacheManager()
EhCacheManager em = new EhCacheManager();
em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
return em;
@Bean(name = "myShiroRealm")
public MyShiroRealm myShiroRealm(EhCacheManager cacheManager)
MyShiroRealm realm = new MyShiroRealm();
realm.setCacheManager(cacheManager);
return realm;
/**
* 注册DelegatingFilterProxy(Shiro)
* 集成Shiro有2种方法:
* 1. 按这个方法自己组装一个FilterRegistrationBean(这种方法更为灵活,可以自己定义UrlPattern,
* 在项目使用中你可能会因为一些很但疼的问题最后采用它, 想使用它你可能需要看官网或者已经很了解Shiro的处理原理了)
* 2. 直接使用ShiroFilterFactoryBean(这种方法比较简单,其内部对ShiroFilter做了组装工作,无法自己定义UrlPattern,
* 默认拦截 /*)
*
* @param dispatcherServlet
* @return
* @author SHANHY
* @create 2016年1月13日
*/
// @Bean
// public FilterRegistrationBean filterRegistrationBean()
// FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
// filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
// // 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
// filterRegistration.addInitParameter("targetFilterLifecycle", "true");
// filterRegistration.setEnabled(true);
// filterRegistration.addUrlPatterns("/*");// 可以自己灵活的定义很多,避免一些根本不需要被Shiro处理的请求被包含进来
// return filterRegistration;
//
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor()
return new LifecycleBeanPostProcessor();
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator()
DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
daap.setProxyTargetClass(true);
return daap;
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(MyShiroRealm myShiroRealm)
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
dwsm.setRealm(myShiroRealm);
// <!-- 用户授权/认证信息Cache, 采用EhCache 缓存 -->
dwsm.setCacheManager(getEhCacheManager());
return dwsm;
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager)
AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
aasa.setSecurityManager(securityManager);
return aasa;
/**
* 加载shiroFilter权限控制规则(从数据库读取然后配置)
*
* @author SHANHY
* @create 2016年1月14日
*/
private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean, StudentService stuService, IScoreDao scoreDao)
/// 下面这些规则配置最好配置到配置文件中 ///
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
filterChainDefinitionMap.put("/user", "authc");// 这里为了测试,只限制/user,实际开发中请修改为具体拦截的请求规则
// anon:它对应的过滤器里面是空的,什么都没做
logger.info("##################从数据库读取权限规则,加载到shiroFilter中##################");
filterChainDefinitionMap.put("/user/edit/**", "authc,perms[user:edit]");// 这里为了测试,固定写死的值,也可以从数据库或其他配置中读取
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/**", "anon");//anon 可以理解为不拦截
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
/**
* ShiroFilter<br/>
* 注意这里参数中的 StudentService 和 IScoreDao 只是一个例子,因为我们在这里可以用这样的方式获取到相关访问数据库的对象,
* 然后读取数据库相关配置,配置到 shiroFilterFactoryBean 的访问规则中。实际项目中,请使用自己的Service来处理业务逻辑。
*
* @param myShiroRealm
* @param stuService
* @param scoreDao
* @return
* @author SHANHY
* @create 2016年1月14日
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager, StudentService stuService, IScoreDao scoreDao)
ShiroFilterFactoryBean shiroFilterFactoryBean = new MShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的连接
shiroFilterFactoryBean.setSuccessUrl("/user");
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
loadShiroFilterChain(shiroFilterFactoryBean, stuService, scoreDao);
return shiroFilterFactoryBean;
/**
* 继承 ShiroFilterFactoryBean 处理拦截资源文件问题。
*
* @author 单红宇(365384722)
* @myblog http://blog.csdn.net/catoop/
* @create 2016年3月8日
*/
public class MShiroFilterFactoryBean extends ShiroFilterFactoryBean
// 对ShiroFilter来说,需要直接忽略的请求
private Set<String> ignoreExt;
public MShiroFilterFactoryBean()
super();
ignoreExt = new HashSet<>();
ignoreExt.add(".jpg");
ignoreExt.add(".png");
ignoreExt.add(".gif");
ignoreExt.add(".bmp");
ignoreExt.add(".js");
ignoreExt.add(".css");
@Override
protected AbstractShiroFilter createInstance() throws Exception
SecurityManager securityManager = getSecurityManager();
if (securityManager == null)
String msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
if (!(securityManager instanceof WebSecurityManager))
String msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
FilterChainManager manager = createFilterChainManager();
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new MSpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
private final class MSpringShiroFilter extends AbstractShiroFilter
protected MSpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver)
super();
if (webSecurityManager == null)
throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
setSecurityManager(webSecurityManager);
if (resolver != null)
setFilterChainResolver(resolver);
@Override
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain chain) throws ServletException, IOException
HttpServletRequest request = (HttpServletRequest)servletRequest;
String str = request.getRequestURI().toLowerCase();
// 因为ShiroFilter 拦截所有请求(在上面我们配置了urlPattern 为 * ,当然你也可以在那里精确的添加要处理的路径,这样就不需要这个类了),而在每次请求里面都做了session的读取和更新访问时间等操作,这样在集群部署session共享的情况下,数量级的加大了处理量负载。
// 所以我们这里将一些能忽略的请求忽略掉。
// 当然如果你的集群系统使用了动静分离处理,静态资料的请求不会到Filter这个层面,便可以忽略。
boolean flag = true;
int idx = 0;
if(( idx = str.indexOf(".")) > 0)
str = str.substring(idx);
if(ignoreExt.contains(str.toLowerCase()))
flag = false;
if(flag)
super.doFilterInternal(servletRequest, servletResponse, chain);
else
chain.doFilter(servletRequest, servletResponse);
其中的 ehcache-shiro.xml 在 src/main/resources 下面,内容为:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" name="shiroCache">
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
</ehcache>
4、继承 AuthorizingRealm 实现认证和授权2个方法
MyShiroRealm.java
package org.springboot.sample.security;
import java.util.List;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
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.authc.UsernamePasswordToken;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springboot.sample.dao.IUserDao;
import org.springboot.sample.entity.Role;
import org.springboot.sample.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* MyShiroRealm
*
* @author 单红宇(365384722)
* @myblog http://blog.csdn.net/catoop/
* @create 2016年1月13日
*/
public class MyShiroRealm extends AuthorizingRealm
private static final Logger logger = LoggerFactory.getLogger(MyShiroRealm.class);
@Autowired
private IUserDao userDao;
/**
* 权限认证,为当前登录的Subject授予角色和权限
* @see 经测试:本例中该方法的调用时机为需授权资源被访问时
* @see 经测试:并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例中默认并未启用AuthorizationCache
* @see 经测试:如果连续访问同一个URL(比如刷新),该方法不会被重复调用,Shiro有一个时间间隔(也就是cache时间,在ehcache-shiro.xml中配置),超过这个时间间隔再刷新页面,该方法会被执行
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)
logger.info("##################执行Shiro权限认证##################");
//获取当前登录输入的用户名,等价于(String) principalCollection.fromRealm(getName()).iterator().next();
String loginName = (String)super.getAvailablePrincipal(principalCollection);
//到数据库查是否有此对象
User user=userDao.findByName(loginName);// 实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
if(user!=null)
//权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission)
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//用户的角色集合
info.setRoles(user.getRolesName());
//用户的角色对应的所有权限,如果只使用角色定义访问权限,下面的四行可以不要
List<Role> roleList=user.getRoleList();
for (Role role : roleList)
info.addStringPermissions(role.getPermissionsName());
// 或者按下面这样添加
//添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色
// simpleAuthorInfo.addRole("admin");
//添加权限
// simpleAuthorInfo.addStringPermission("admin:manage");
// logger.info("已为用户[mike]赋予了[admin]角色和[admin:manage]权限");
return info;
// 返回null的话,就会导致任何用户访问被拦截的请求时,都会自动跳转到unauthorizedUrl指定的地址
return null;
/**
* 登录认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authenticationToken) throws AuthenticationException
//UsernamePasswordToken对象用来存放提交的登录信息
UsernamePasswordToken token=(UsernamePasswordToken) authenticationToken;
logger.info("验证当前Subject时获取到token为:" + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));
//查出是否有此用户
User user=userDao.findByName(token.getUsername());
if(user!=null)
// 若存在,将此用户存放到登录认证info中,无需自己做密码对比,Shiro会为我们进行密码对比校验
return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
return null;
注意:其中 userDao.findByName 这个代码就不贴上了,也没啥可贴的,根据姓名查询一个对象而已。
5、编写测试的 Controller 和测试 jsp 页面
ShiroController.java
package org.springboot.sample.controller;
import java.util.Map;
import javax.validation.Valid;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springboot.sample.dao.IUserDao;
import org.springboot.sample.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
/**
* Shiro测试Controller
*
* @author 单红宇(365384722)
* @myblog http://blog.csdn.net/catoop/
* @create 2016年1月13日
*/
@Controller
public class ShiroController
private static final Logger logger = LoggerFactory.getLogger(ShiroController.class);
@Autowired
private IUserDao userDao;
@RequestMapping(value="/login",method=RequestMethod.GET)
public String loginForm(Model model)
model.addAttribute("user", new User());
return "login";
@RequestMapping(value="/login",method=RequestMethod.POST)
public String login(@Valid User user,BindingResult bindingResult,RedirectAttributes redirectAttributes)
if(bindingResult.hasErrors())
return "login";
String username = user.getUsername();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
//获取当前的Subject
Subject currentUser = SecurityUtils.getSubject();
try
//在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm执行必须的认证检查
//每个Realm都能在必要时对提交的AuthenticationTokens作出反应
//所以这一步在调用login(token)方法时,它会走到MyRealm.doGetAuthenticationInfo()方法中,具体验证方式详见此方法
logger.info("对用户[" + username + "]进行登录验证..验证开始");
currentUser.login(token);
logger.info("对用户[" + username + "]进行登录验证..验证通过");
catch(UnknownAccountException uae)
logger.info("对用户[" + username + "]进行登录验证..验证未通过,未知账户");
redirectAttributes.addFlashAttribute("message", "未知账户");
catch(IncorrectCredentialsException ice)
logger.info("对用户[" + username + "]进行登录验证..验证未通过,错误的凭证");
redirectAttributes.addFlashAttribute("message", "密码不正确");
catch(LockedAccountException lae)
logger.info("对用户[" + username + "]进行登录验证..验证未通过,账户已锁定");
redirectAttributes.addFlashAttribute("message", "账户已锁定")十 Spring Boot Shiro 权限管理