4.0.0
org.springframework.boot
spring-boot-starter-parent
2.3.0.RELEASE
com.cxb
shiro
0.0.1-SNAPSHOT
shiro
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-thymeleaf
net.sourceforge.nekohtml
nekohtml
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
5.1.38
org.springframework.boot
spring-boot-starter-test
test
org.apache.shiro
shiro-spring
1.4.0
org.springframework.boot
spring-boot-maven-plugin
spring:
datasource:
url: jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
jpa:
database: mysql
show-sql: true
hibernate:
ddl-auto: update
naming:
strategy: org.hibernate.cfg.DefaultComponentSafeNamingStrategy
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
thymeleaf:
cache: false
mode: LEGACYHTML5
prefix: classpath:/templates/
suffix: .html
package com.cxb.shiro.entity;
import java.io.Serializable;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
@Entity
public class UserInfo implements Serializable {
/**
*
*/
private static final long serialVersionUID = 2408336391165537166L;
@Id
@GeneratedValue
private Integer uid;
@Column(unique = true)
private String username;
private String name;
private String password;
private String salt;
private byte state;
@ManyToMany(fetch=FetchType.EAGER)
@JoinTable(name = "SysUserRole", joinColumns = { @JoinColumn(name = "uid") }, inverseJoinColumns = { @JoinColumn(name = "roleId") })
private List roles;
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public byte getState() {
return state;
}
public void setState(byte state) {
this.state = state;
}
public List getRoles() {
return roles;
}
public void setRoles(List roles) {
this.roles = roles;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
/**
* 密码盐.
*
* @return
*/
public String getCredentialsSalt() {
//重新对盐重新进行了定义,用户名+salt,这样就更加不容易被破解
//return this.username + this.salt;
//为了测试方便,我们简单定义盐为frank
String salt = "frank";
return salt;
}
}
package com.cxb.shiro.entity;
import java.io.Serializable;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
@Entity
public class SysRole implements Serializable {
/**
*
*/
private static final long serialVersionUID = 2662493471572546262L;
@Id
@GeneratedValue
private Integer id;
private String role;
private String description;
private Boolean available = Boolean.FALSE;
@ManyToMany(fetch=FetchType.EAGER)
@JoinTable(name = "SysRolePermission", joinColumns = { @JoinColumn(name = "roleId") }, inverseJoinColumns = { @JoinColumn(name = "permissionId") })
private List permissions;
@ManyToMany
@JoinTable(name = "SysUserRole", joinColumns = { @JoinColumn(name = "roleId") }, inverseJoinColumns = { @JoinColumn(name = "uid") })
private List userInfos;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Boolean getAvailable() {
return available;
}
public void setAvailable(Boolean available) {
this.available = available;
}
public List getPermissions() {
return permissions;
}
public void setPermissions(List permissions) {
this.permissions = permissions;
}
public List getUserInfos() {
return userInfos;
}
public void setUserInfos(List userInfos) {
this.userInfos = userInfos;
}
}
package com.cxb.shiro.entity;
import java.io.Serializable;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
@Entity
public class SysPermission implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1708879712215410035L;
@Id
@GeneratedValue
private Integer id;
private String name;
@Column(columnDefinition = "enum('menu','button')")
private String resourceType;
private String url;
private String permission;
private Long parentId;
private String parentIds;
private Boolean available = Boolean.FALSE;
@ManyToMany
@JoinTable(name = "SysRolePermission", joinColumns = { @JoinColumn(name = "permissionId") }, inverseJoinColumns = { @JoinColumn(name = "roleId") })
private List roles;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getResourceType() {
return resourceType;
}
public void setResourceType(String resourceType) {
this.resourceType = resourceType;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public String getParentIds() {
return parentIds;
}
public void setParentIds(String parentIds) {
this.parentIds = parentIds;
}
public Boolean getAvailable() {
return available;
}
public void setAvailable(Boolean available) {
this.available = available;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
public List getRoles() {
return roles;
}
public void setRoles(List roles) {
this.roles = roles;
}
}
package com.cxb.shiro.dao;
import com.cxb.shiro.entity.UserInfo;
import org.springframework.data.repository.CrudRepository;
public interface UserInfoDao extends CrudRepository{
UserInfo findByUsername(String username);
}
package com.cxb.shiro.service;
import com.cxb.shiro.entity.UserInfo;
public interface UserInfoService {
UserInfo findByUsername(String username);
}
package com.cxb.shiro.service.impl;
import javax.annotation.Resource;
import com.cxb.shiro.dao.UserInfoDao;
import com.cxb.shiro.entity.UserInfo;
import com.cxb.shiro.service.UserInfoService;
import org.springframework.stereotype.Service;
@Service
public class UserInfoServiceImpl implements UserInfoService {
@Resource
private UserInfoDao dao;
public UserInfo findByUsername(String username) {
return dao.findByUsername(username);
}
}
package com.cxb.shiro.config;
import javax.annotation.Resource;
import com.cxb.shiro.entity.SysPermission;
import com.cxb.shiro.entity.SysRole;
import com.cxb.shiro.entity.UserInfo;
import com.cxb.shiro.service.UserInfoService;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
public class MyShiroRealm extends AuthorizingRealm {
@Resource
private UserInfoService userInfoService;
/**
* 权限配置
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("MyShiroRealm->doGetAuthorizationInfo");
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal();
for (SysRole role : userInfo.getRoles()) {
authorizationInfo.addRole(role.getRole());
for (SysPermission p : role.getPermissions()) {
authorizationInfo.addStringPermission(p.getPermission());
}
}
return authorizationInfo;
}
/**
* 主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
System.out.println("MyShiroRealm->doGetAuthenticationInfo");
//用户名
String username = (String) token.getPrincipal();
System.out.println("username:" + username);
//获取用户信息
UserInfo userInfo = userInfoService.findByUsername(username);
if (userInfo == null) {
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userInfo,
userInfo.getPassword(), ByteSource.Util.bytes(userInfo.getCredentialsSalt()),
getName());
return authenticationInfo;
}
}
package com.cxb.shiro.config;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
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 org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//拦截器 从上到下执行
Map filterChainDefinitionMap = new LinkedHashMap();
//不被拦截
filterChainDefinitionMap.put("/static/**", "anon");
//配置退出 shiro已经帮我们实现退出的代码
filterChainDefinitionMap.put("/logout", "logout");
//需要权限验证
filterChainDefinitionMap.put("/**", "authc");
//登陆页面
shiroFilterFactoryBean.setLoginUrl("/login");
//登陆成功跳转页面
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授权页面
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 凭证匹配器((由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了)
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMather = new HashedCredentialsMatcher();
hashedCredentialsMather.setHashAlgorithmName("md5"); //散列算法:这里使用MD5算法;
hashedCredentialsMather.setHashIterations(1); //散列的次数,如果散列两次,相当于 md5(md5(""));
return hashedCredentialsMather;
}
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
/**
* 开启shiro aop注解支持
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean(name="simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("DatabaseException", "databaseError");
mappings.setProperty("UnauthorizedException", "403");
r.setExceptionMappings(mappings);
r.setDefaultErrorView("error");
r.setExceptionAttribute("ex");
return r;
}
}
package com.cxb.shiro.web;
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 map) {
System.out.println("HomeController->login");
String exception = (String)request.getAttribute("shiroLoginFailure");
System.out.println("exception="+exception);
String msg = "";
if(exception!=null) {
if(UnknownAccountException.class.getName().equals(exception)) {
msg = "UnknownAccountException->账号不存在";
} else if(IncorrectCredentialsException.class.getName().equals(exception)) {
msg = "IncorrectCredentialsException->密码不正确";
} else if("kaptchaValidateFailed".equals(exception)) {
msg = "验证码错误";
} else {
msg = " else ->"+exception;
}
map.put("msg", msg);
return "/login";
}
map.put("msg", "login success");
return "/index";
}
@RequestMapping("/403")
public String unauthorizedRole() {
System.out.println("HomeController->unauthorizedRole");
return "/403";
}
}
package com.cxb.shiro.web;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class UserInfoController {
/**
* 用户查询.
* @return
*/
@RequestMapping("/userList")
@RequiresPermissions("userInfo:view")//权限管理;
public String userInfo(){
System.out.println("UserInfoController->userInfo");
return "userInfo";
}
/**
* 用户添加;
* @return
*/
@RequestMapping("/userAdd")
@RequiresPermissions("userInfo:add")//权限管理;
public String userInfoAdd(){
System.out.println("UserInfoController->userInfoAdd");
return "userInfoAdd";
}
/**
* 用户删除;
* @return
*/
@RequestMapping("/userDel")
@RequiresPermissions("userInfo:del")//权限管理;
public String userDel() throws Exception{
System.out.println("UserInfoController->userDel");
return "userInfoDel";
}
}
package com.cxb.shiro;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ShiroApplication {
public static void main(String[] args) {
SpringApplication.run(ShiroApplication.class, args);
}
}
-- --------------------------------------------------------
-- 主机: 127.0.0.1
-- 服务器版本: 5.6.40 - MySQL Community Server (GPL)
-- 服务器操作系统: Win64
-- HeidiSQL 版本: 8.2.0.4675
-- --------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!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' */;
-- 导出 表 shiro.sys_permission 结构
CREATE TABLE IF NOT EXISTS `sys_permission` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`available` bit(1) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`parent_id` bigint(20) DEFAULT NULL,
`parent_ids` varchar(255) DEFAULT NULL,
`permission` varchar(255) DEFAULT NULL,
`resource_type` enum('menu','button') DEFAULT NULL,
`url` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- 正在导出表 shiro.sys_permission 的数据:~0 rows (大约)
DELETE FROM `sys_permission`;
/*!40000 ALTER TABLE `sys_permission` DISABLE KEYS */;
INSERT INTO `sys_permission` (`id`, `available`, `name`, `parent_id`, `parent_ids`, `permission`, `resource_type`, `url`) VALUES
(1, b'1', 'frank', 1, '1', 'userInfo:view', 'menu', '/userList/**');
/*!40000 ALTER TABLE `sys_permission` ENABLE KEYS */;
-- 导出 表 shiro.sys_role 结构
CREATE TABLE IF NOT EXISTS `sys_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`available` bit(1) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`role` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- 正在导出表 shiro.sys_role 的数据:~0 rows (大约)
DELETE FROM `sys_role`;
/*!40000 ALTER TABLE `sys_role` DISABLE KEYS */;
INSERT INTO `sys_role` (`id`, `available`, `description`, `role`) VALUES
(1, b'1', NULL, 'admin');
/*!40000 ALTER TABLE `sys_role` ENABLE KEYS */;
-- 导出 表 shiro.sys_role_permission 结构
CREATE TABLE IF NOT EXISTS `sys_role_permission` (
`permission_id` int(11) NOT NULL,
`role_id` int(11) NOT NULL,
KEY `FK9q28ewrhntqeipl1t04kh1be7` (`role_id`),
KEY `FKomxrs8a388bknvhjokh440waq` (`permission_id`),
CONSTRAINT `FK9q28ewrhntqeipl1t04kh1be7` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`),
CONSTRAINT `FKomxrs8a388bknvhjokh440waq` FOREIGN KEY (`permission_id`) REFERENCES `sys_permission` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 正在导出表 shiro.sys_role_permission 的数据:~0 rows (大约)
DELETE FROM `sys_role_permission`;
/*!40000 ALTER TABLE `sys_role_permission` DISABLE KEYS */;
INSERT INTO `sys_role_permission` (`permission_id`, `role_id`) VALUES
(1, 1);
/*!40000 ALTER TABLE `sys_role_permission` ENABLE KEYS */;
-- 导出 表 shiro.sys_user_role 结构
CREATE TABLE IF NOT EXISTS `sys_user_role` (
`role_id` int(11) NOT NULL,
`uid` int(11) NOT NULL,
KEY `FKgkmyslkrfeyn9ukmolvek8b8f` (`uid`),
KEY `FKhh52n8vd4ny9ff4x9fb8v65qx` (`role_id`),
CONSTRAINT `FKgkmyslkrfeyn9ukmolvek8b8f` FOREIGN KEY (`uid`) REFERENCES `user_info` (`uid`),
CONSTRAINT `FKhh52n8vd4ny9ff4x9fb8v65qx` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 正在导出表 shiro.sys_user_role 的数据:~0 rows (大约)
DELETE FROM `sys_user_role`;
/*!40000 ALTER TABLE `sys_user_role` DISABLE KEYS */;
INSERT INTO `sys_user_role` (`role_id`, `uid`) VALUES
(1, 1);
/*!40000 ALTER TABLE `sys_user_role` ENABLE KEYS */;
-- 导出 表 shiro.user_info 结构
CREATE TABLE IF NOT EXISTS `user_info` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`salt` varchar(255) DEFAULT NULL,
`state` tinyint(4) NOT NULL,
`username` varchar(255) DEFAULT NULL,
PRIMARY KEY (`uid`),
UNIQUE KEY `UK_f2ksd6h8hsjtd57ipfq9myr64` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- 正在导出表 shiro.user_info 的数据:~0 rows (大约)
DELETE FROM `user_info`;
/*!40000 ALTER TABLE `user_info` DISABLE KEYS */;
INSERT INTO `user_info` (`uid`, `name`, `password`, `salt`, `state`, `username`) VALUES
(1, 'frank', 'd40fdd323f5322ff34a41f026f35cf20', 'frank', 1, 'frank');
/*!40000 ALTER TABLE `user_info` ENABLE KEYS */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
403.html
Title
403没有权限
index.html
Title
index
登陆信息:
login.html
Title
错误信息:
userInfo.html
Title
用户查询界面
userInfoAdd.html
Title
用户添加界面
userInfoDel.html
Title
用户删除界面
以上页面只是测试页面,只用来标题区分权限问题。
因为该用户只有一个userInfo:view权限,只能查看userList接口。
项目下载:https://github.com/ChenXbFrank/springboot_shiro