1、boot-shrio 建库脚本:
DROP TABLE IF EXISTS `u_permission`;
CREATE TABLE IF NOT EXISTS `u_permission` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`url` varchar(256) DEFAULT NULL COMMENT 'url地址',
`name` varchar(64) DEFAULT NULL COMMENT 'url描述',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- 数据导出被取消选择。
-- 导出 表 boot_shrio.u_role 结构
DROP TABLE IF EXISTS `u_role`;
CREATE TABLE IF NOT EXISTS `u_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL COMMENT '角色名称',
`type` varchar(10) DEFAULT NULL COMMENT '角色类型',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- 数据导出被取消选择。
-- 导出 表 boot_shrio.u_role_permission 结构
DROP TABLE IF EXISTS `u_role_permission`;
CREATE TABLE IF NOT EXISTS `u_role_permission` (
`rid` bigint(20) DEFAULT NULL COMMENT '角色ID',
`pid` bigint(20) DEFAULT NULL COMMENT '权限ID'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- 数据导出被取消选择。
-- 导出 表 boot_shrio.u_user 结构
DROP TABLE IF EXISTS `u_user`;
CREATE TABLE IF NOT EXISTS `u_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`nickname` varchar(20) DEFAULT NULL COMMENT '用户昵称',
`email` varchar(128) DEFAULT NULL COMMENT '邮箱|登录帐号',
`pswd` varchar(32) DEFAULT NULL COMMENT '密码',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',
`status` bigint(1) DEFAULT '1' COMMENT '1:有效,0:禁止登录',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- 数据导出被取消选择。
-- 导出 表 boot_shrio.u_user_role 结构
DROP TABLE IF EXISTS `u_user_role`;
CREATE TABLE IF NOT EXISTS `u_user_role` (
`uid` bigint(20) DEFAULT NULL COMMENT '用户ID',
`rid` bigint(20) DEFAULT NULL COMMENT '角色ID'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- 正在导出表 boot_shrio.u_permission 的数据:~2 rows (大约)
/*!40000 ALTER TABLE `u_permission` DISABLE KEYS */;
INSERT INTO `u_permission` (`id`, `url`, `name`) VALUES
(1, 'userInfo/userList', '用户管理'),
(2, 'userInfo/userAdd', '用户添加'),
(3, 'userInfo/userDel', '用户删除');
/*!40000 ALTER TABLE `u_permission` ENABLE KEYS */;
-- 正在导出表 boot_shrio.u_role 的数据:~2 rows (大约)
/*!40000 ALTER TABLE `u_role` DISABLE KEYS */;
INSERT INTO `u_role` (`id`, `name`, `type`) VALUES
(1, '管理员', '1'),
(2, '用户', '2');
/*!40000 ALTER TABLE `u_role` ENABLE KEYS */;
-- 正在导出表 boot_shrio.u_role_permission 的数据:~2 rows (大约)
/*!40000 ALTER TABLE `u_role_permission` DISABLE KEYS */;
INSERT INTO `u_role_permission` (`rid`, `pid`) VALUES
(1, 1),
(1, 2),
(1, 3);
/*!40000 ALTER TABLE `u_role_permission` ENABLE KEYS */;
-- 正在导出表 boot_shrio.u_user 的数据:~0 rows (大约)
/*!40000 ALTER TABLE `u_user` DISABLE KEYS */;
INSERT INTO `u_user` (`id`, `nickname`, `email`, `pswd`, `create_time`, `last_login_time`, `status`) VALUES
(1, 'admin', 'admin@163.com', '1', '2019-04-27 22:44:44', NULL, 1);
/*!40000 ALTER TABLE `u_user` ENABLE KEYS */;
-- 正在导出表 boot_shrio.u_user_role 的数据:~0 rows (大约)
/*!40000 ALTER TABLE `u_user_role` DISABLE KEYS */;
INSERT INTO `u_user_role` (`uid`, `rid`) VALUES
(1, 1);
第二步:数据库实体、Mapper文件、Mapper接口、Service接口和Service 实现:
具体请参考:
apache shrio 依赖jar 文件pom.xml
<!--apache shrio 依赖jar -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.5</version>
</dependency>
boot-shrio-controller 项目涉及apache shrio 配置和用户登入:
package com.zzg.shrio.config;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import com.zzg.shrio.realm.ShiroRealm;
/**
* shrio 配置类
* @author Administrator
*
*/
@Configuration
public class ShiroConfiguration {
// 管理shrio 生命周期
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* HashedCredentialsMatcher,这个类是为了对密码进行编码的,
* 防止密码在数据库里明码保存,当然在登陆认证的时候,
* 这个类也负责对form里输入的密码进行编码。
*/
@Bean(name = "hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("MD5");
credentialsMatcher.setHashIterations(2);
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}
/**ShiroRealm,这是个自定义的认证类,继承自AuthorizingRealm,
* 负责用户的认证和权限的处理,可以参考JdbcRealm的实现。
*/
@Bean(name = "shiroRealm")
@DependsOn("lifecycleBeanPostProcessor")
public ShiroRealm shiroRealm() {
ShiroRealm realm = new ShiroRealm();
// realm.setCredentialsMatcher(hashedCredentialsMatcher());
return realm;
}
// /**
// * EhCacheManager,缓存管理,用户登陆成功后,把用户信息和权限信息缓存起来,
// * 然后每次用户请求时,放入用户的session中,如果不设置这个bean,每个请求都会查询一次数据库。
// */
// @Bean(name = "ehCacheManager")
// @DependsOn("lifecycleBeanPostProcessor")
// public EhCacheManager ehCacheManager() {
// return new EhCacheManager();
// }
/**
* SecurityManager,权限管理,这个类组合了登陆,登出,权限,session的处理,是个比较重要的类。
// */
//SecurityManager 是 Shiro 架构的核心,通过它来链接Realm和用户(文档中称之为Subject.)
@Bean(name = "securityManager")
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm());
// securityManager.setCacheManager(ehCacheManager());
return securityManager;
}
/**
* ShiroFilterFactoryBean,是个factorybean,为了生成ShiroFilter。
* 它主要保持了三项数据,securityManager,filters,filterChainDefinitionManager。
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager());
//拦截器.
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/static/**", "anon");
//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
//<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
//<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterChainDefinitionMap.put("/**", "authc");
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
*/
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
/**
* AuthorizationAttributeSourceAdvisor,shiro里实现的Advisor类,
* 内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor aASA = new AuthorizationAttributeSourceAdvisor();
aASA.setSecurityManager(securityManager());
return aASA;
}
}
package com.zzg.shrio.realm;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.shiro.SecurityUtils;
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.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.zzg.entity.Permission;
import com.zzg.entity.Role;
import com.zzg.entity.RolePermission;
import com.zzg.entity.User;
import com.zzg.entity.UserRole;
import com.zzg.service.PermissionService;
import com.zzg.service.RolePermissionService;
import com.zzg.service.RoleService;
import com.zzg.service.UserRoleService;
import com.zzg.service.UserService;
public class ShiroRealm extends AuthorizingRealm {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private UserService userService;
@Autowired
private PermissionService permissionService;
@Autowired
private UserRoleService userRoleService;
@Autowired
private RoleService roleService;
@Autowired
private RolePermissionService rolePermissionService;
// 用户赋于相关权限
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// TODO Auto-generated method stub
logger.info("doGetAuthorizationInfo+" + principalCollection.toString());
User user = userService.getByUserName((String) principalCollection.getPrimaryPrincipal());
// 把principals放session中 key=userId value=principals
SecurityUtils.getSubject().getSession().setAttribute(String.valueOf(user.getId()),
SecurityUtils.getSubject().getPrincipals());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 赋予角色
List<UserRole> userRoles = userRoleService.getByUid(user.getId());
List<Long> list = userRoles.stream().map(UserRole::getRid).collect(Collectors.toList());
List<Role> roles = roleService.getByIds(list);
for (Role role : roles) {
info.addRole(role.getName());
}
// 赋予权限
List<Long> roleIds = roles.stream().map(Role::getId).collect(Collectors.toList());
List<RolePermission> rolePermissions = rolePermissionService.getByRoleIds(roleIds);
List<Long> permissionIds = rolePermissions.stream().map(RolePermission::getPid).collect(Collectors.toList());
List<Permission> permissions = permissionService.getByPermissionIds(permissionIds);
for (Permission permission : permissions) {
info.addStringPermission(permission.getUrl());
}
return info;
}
// 用户登入验证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
// TODO Auto-generated method stub
logger.info("doGetAuthenticationInfo +" + authenticationToken.toString());
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String userName = token.getUsername();
// 输出用户信息(账户和密码)
logger.info(userName + token.getPassword());
User user = userService.getByUserName(token.getUsername());
if (user != null) {
// 设置用户session
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute("user", user);
return new SimpleAuthenticationInfo(userName, user.getPswd(), getName());
}
return null;
}
}
使用Apache shrio 实现权限鉴权业务代码:
package com.zzg.controller;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HomeController {
@RequestMapping({"/","/index"})
public String index() {
return "index";
}
@RequestMapping("/login")
public String login(HttpServletRequest request, Map<String, Object> map) {
System.out.println("HomeController.login");
// 登录失败从request中获取shiro处理的异常信息。
// shiroLoginFailure:就是shiro异常类的全类名.
Object exception = request.getAttribute("shiroLoginFailure");
String msg = "";
if (exception != null) {
if (UnknownAccountException.class.isInstance(exception)) {
System.out.println("账户不存在");
msg = "账户不存在或密码不正确";
} else if (IncorrectCredentialsException.class.isInstance(exception)) {
System.out.println("密码不正确");
msg = "账户不存在或密码不正确";
} else {
System.out.println("其他异常");
msg = "其他异常";
}
}
map.put("msg", msg);
// 此方法不处理登录成功,由shiro进行处理.
return "login";
}
@RequestMapping("/403")
public String unauthorizedRole(){
System.out.println("------没有权限-------");
return "403";
}
}
package com.zzg.controller;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/userInfo")
public class UserInfoController {
/**
* 用户查询.
* @return
*/
@RequestMapping("/userList")
@RequiresPermissions("userInfo/userList")//权限管理;
public String userInfo(){
return "userInfo";
}
/**
* 用户添加;
* @return
*/
@RequestMapping("/userAdd")
@RequiresPermissions("userInfo/userAdd")//权限管理;
public String userInfoAdd(){
return "userInfoAdd";
}
/**
* 用户删除;
* @return
*/
@RequestMapping("/userDel")
@RequiresPermissions("userInfo/userDel")//权限管理;
public String userDel(){
return "userInfoDel";
}
}
springboot +thymelf 具体参考: