Shiro说明
Shiro 是一个强大、简单易用的 Java 安全框架,主要用来更便捷的认证,授权,加密,会话管理等,可为任何应用提供安全保障,今天主要介绍 Shiro 的认证和授权功能。
三大核心组件
Subject
:认证主体。它包含两个信息:Principals
和Credentials
Principals
:身份。可以是用户名,邮件,手机号码等等,用来标识一个登录主体身份
Credentials
:凭证。常见有密码,数字证书等等SecurityManager
:安全管理员,是Shiro架构的核心Realm
:Realms 是一个域,它是连接Shiro和具体应用的桥梁
集成Shiro过程
- 引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
</dependencies>
- 建立权限数据库表
这里我们给出表结构,大家可以自行设计
##权限表
CREATE TABLE `hotel_permission` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parentId` int(11) DEFAULT NULL,
`permissName` varchar(50) DEFAULT NULL,
`permissNice` varchar(50) DEFAULT NULL,
`permissType` varchar(2) DEFAULT NULL,
`permissUri` varchar(50) DEFAULT NULL,
`crtUserId` int(11) DEFAULT NULL,
`crtTime` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
##角色表
CREATE TABLE `hotel_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`roleName` varchar(50) DEFAULT NULL,
`description` varchar(100) DEFAULT NULL,
`crtUserId` int(20) DEFAULT NULL,
`crtTime` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
##用户表
CREATE TABLE `hotel_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`sex` varchar(2) DEFAULT NULL,
`age` varchar(3) DEFAULT NULL,
`address` varchar(100) DEFAULT NULL,
`crtUserId` int(20) NOT NULL,
`crtTime` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
##角色权限关联表
CREATE TABLE `hotel_role_permiss` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`roleId` int(11) DEFAULT NULL,
`permissId` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
##用户角色关联表
CREATE TABLE `hotel_user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userId` int(11) DEFAULT NULL,
`roleId` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
3)自定义Realm
有了数据库表和数据之后,我们开始自定义 realm,自定义 realm 需要继承 AuthorizingRealm 类,因
为该类封装了很多方法,它也是一步步继承自 Realm 类的,继承了 AuthorizingRealm 类后,需要重写
两个方法
doGetAuthenticationInfo() 方法:用来验证当前登录的用户,获取认证信息
doGetAuthorizationInfo() 方法:用来为当前登陆成功的用户授予权限和角色
public class RealmShiro extends AuthorizingRealm {
@Autowired
private IHotelPermissionService permissionService;
@Autowired
private IHotelRoleService roleService;
@Autowired
private IHotelUserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
HotelUser user = (HotelUser) principal.getPrimaryPrincipal();
Integer userId = user.getId();
Object object = SecurityUtils.getSubject().getSession().getAttribute(String.valueOf(userId));
HotelUser userShiro = (HotelUser)object;
Set<String> roleSet = userShiro.getRoles();
Set<String> authSet = userShiro.getPermiss();
SimpleAuthorizationInfo authorInfo = new SimpleAuthorizationInfo();
authorInfo.setRoles(roleSet);
authorInfo.setStringPermissions(authSet);
return authorInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upt = (UsernamePasswordToken) token;
String username = upt.getUsername();
HotelUser user = userService.queryByUserName(username);
if(null == user){
throw new UnknownAccountException();
}
Integer userId = user.getId();
user.setRoles(roleService.queryUserRoleByUserId(userId));
user.setPermiss(permissionService.queryUserPermissByUserId(userId));
SecurityUtils.getSubject().getSession().setAttribute(String.valueOf(userId),user);
SimpleAuthenticationInfo authenticationInfo =
new SimpleAuthenticationInfo(user,user.getPassword(),getName());
return authenticationInfo;
}
}
- Shiro 配置
@Configuration
public class ShiroConfig {
@Bean(value = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/");
shiroFilterFactoryBean.setSuccessUrl("/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/unAuth");
Map<String,String> filterMap = new LinkedHashMap<>();
filterMap.put("/static/**","anon");
filterMap.put("/templates/**","anon");
filterMap.put("/login","anon");
filterMap.put("/","anon");
filterMap.put("/loginOut","logout");
filterMap.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
@Bean(value = "securityManager")
public DefaultWebSecurityManager securityManager(@Qualifier("myShiroRealm")RealmShiro realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
return securityManager;
}
@Bean(value = "myShiroRealm")
public RealmShiro myShiroRealm(){
RealmShiro realmShiro = new RealmShiro();
return realmShiro;
}
@Bean(value = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
//如果要使用 @RequiresPermissions和@RequiresRoles,则此处不可缺少
@Bean
@DependsOn("lifecycleBeanPostProcessor")
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
说明:
认证过滤器:
- anon:表示可以匿名使用
- authc:表示需要认证(登录)才能使用,没有参数
- authcBasic:没有参数表示httpBasic认证 ; user:当登入操作时不做检查
授权过滤器:
- roles:参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割;EQ:
/admins/user/=roles[“admin,guest”]
,每个参数通过才算通过。- perms:参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割;EQ:
/admins/user/=perms[“user:add:,user:modify:”]
,当有多个参数时必须每个参数都通过才通过。
- 账号密码验证及登录功能
@PostMapping("/login")
@ResponseBody
public Object login(String username, String password){
if(StringUtils.isEmpty(username)){
return ResultUtil.error("用户名为空");
}
if(StringUtils.isEmpty(password)){
return ResultUtil.error("密码为空");
}
//注意以下代码
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
subject.login(token);
return ResultUtil.success("登录成功");
}