Introduction:
Shiro is apache's a open source security framework , it will draw security authentication software systems related functions out to achieve user authentication, permissions, authorization , encryption, session management and other functions, to form a common security certification framework , using shiro can Complete the development of authentication, authorization and other functions very quickly, reducing system costs.
Summary structure:
Detailed structure:
The first layer: subject
The second layer: securiryManager- the core of Shiro, used to coordinate the work of management components
Authenticator: Authentication manager - responsible for performing authentication operations
Authorizer: Authorization Manager - responsible for authorization detection
Session manage :
The third layer: realm - Realm is the bridge between shiro and your application security data.
Actual combat:
配置Shiro安全过滤器
WEB-INF / web.xml
<!-- 配置Shiro安全过滤器 --> <filter> <filter-name>DelegatingFilterProxy</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <!-- 初始化参数 --> <init-param> <param-name>targetBeanName</param-name> <param-value>shiroFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>DelegatingFilterProxy</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
spring-configs.xml
<!-- Configure shiro framework-->
<!-- Configure realm object (will be managed by spring) -->
<bean id="userRealm"
class="com.jt.sys.service.realm.ShiroUserRealm">
< !-- Credential matcher (password encryption) -->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5 "/>
<!-- <property name="hashIterations" value="1024"/> -->
</bean>
</property>
</bean>
<!-- Configure the CacheManager object (not necessary, mainly To improve performance,The authentication information and authorization information can be cached) -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<!-- Set a net.sf.ehcache.CacheManager instance here if you already have one. If not, a new one
will be creaed with a default config:
<property name="cacheManager" ref="ehCacheManager"/> -->
<!-- If you don't have a pre-built net.sf.ehcache.CacheManager instance to inject, but you want
a specific Ehcache configuration to be used, specify that here. If you don't, a default
will be used.: -->
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
</bean>
<!-- 配置securityManager对象(此对象时shiro框架核心) -->
<bean id="securityManager"
class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm"/>
<property name="cacheManager" ref="cacheManager"/>
</bean>
<! - configuration ShiroFilter (achieved through this filter arrangement for filtering the resource request, which requests to release, which is to be authenticated) ->
<the bean ID = "shiroFilter" class = "org.apache.shiro.spring.web.ShiroFilterFactoryBean ">
<!-- The core security interface of shiro-->
<property name="securityManager" ref="securityManager"/>
<!-- Connection when logging in is required -->
<property name="loginUrl" value=" /loginUI.do"></property>
<!-- The connection to jump after a successful login (this has been processed in the login) -->
<!-- <property name="successUrl" value="/index.jsp"></property>- ->
<!-- When accessing unauthorized resources, the connection to jump
<property name="unauthorizedUrl" value="/default.html"></property>-->
<!-- shiro connection constraint configuration- ->
<property name="filterChainDefinitions">
<value>
<!-- Allow anonymous access to static resource settings -->
/bower_components/** = anon
/build/** = anon
/dist/** = anon
/plugins /** = anon
/doLogin.do = anon
<!-- Exit-->
/doLogout.do = logout <!-- Subject's logout method will be called, this method will clear the session-->
<! --The remaining other paths must be authenticated before they can be accessed -->
/** = authc
</value>
</property>
</bean>
<!--Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor"
class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!--启用shiro注解权限检查(@RequestPermissions)-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager"
ref="securityManager"/>
</bean>
Certification:
Certification process:
- The system calls the login method of subject to submit the user information token to the SecurityManager
subject.login(token)
// This request will be submitted to SecurityManager
//SecurityManager will call the authentication processor Authenticator
//The authentication processor will access the relevant Realm object to obtain authentication information
- SecurityManager delegates the authentication operation to the authenticator object Authenticator
- The Authenticator passes the identity information to Realm.
SysUser sysUser = sysUserDao.findUserByUserName(username);
- Realm accesses the database to obtain user information, then encapsulates the information and returns it.
//3.2 Encapsulate user information
AuthenticationInfo info
= new SimpleAuthenticationInfo(
sysUser.getUsername(), // Master identity
sysUser.getPassword(), //The encrypted password
byteSource, // The byte source object corresponding to salt
getName()); //realm 's name
5) Authenticator authenticates the information returned by realm.
SysLoginController.java
@Controller @RequestMapping("/") public class SysLoginController { @Autowired private SysUserService sysUserService; @RequestMapping("loginUI") public String loginUI(){ return "login"; } @RequestMapping("doLogin") @ResponseBody public JsonResult doLogin(String username, String password){ String r=DigestUtils.md5DigestAsHex("123456".getBytes()); System.out.println("r="+r); sysUserService.login(username, password); return new JsonResult("login ok"); } }
@Service public class SysUserServiceImpl implements SysUserService { @Autowired private SysUserDao sysUserDao; @Autowired private SysRoleDao sysRoleDao; @Autowired private SysUserRoleDao sysUserRoleDao; @Override public void login(String username,String password) { System.out.println("service.login"); //0.参数合法性验证 if(StringUtils.isEmpty(username)) throw new ServiceException("用户名不能为空"); if(StringUtils.isEmpty(password)) throw new ServiceException("密码不能为空"); //1.获取Subject(主体)对象 Subject subject=SecurityUtils.getSubject(); //2.封装用户名和密码 UsernamePasswordToken token=new UsernamePasswordToken(username, password); //3.执行身份认证 try { subject.login(token); //此请求会提交给SecurityManager //SecurityManager会调用认证处理器Authenticator //认证处理器会去访问相关Realm对象获取认证信息 } catch (AuthenticationException e) { e.printStackTrace(); throw new ServiceException("用户名或密码不正确"); } //4.记录用户信息 Session session= SecurityUtils.getSubject().getSession(); session.setAttribute("user", username); }
Inherit AuthorizingRealm and override doGetAuthenticationInfo
Realize basic authentication and permission control through Realm
public class ShiroUserRealm extends AuthorizingRealm { @Autowired private SysUserDao sysUserDao; /** * 完成认证信息的获取以及封装 * 此方法何时调用?(执行登陆认证时调用) * @param * 用于接收用户身份以及凭证信息的对象(用户输入的) * * @return AuthenticationInfo * 封装了认证信息的对象(从数据库查询到的) * * client-->controller-->service-->realm */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("realm.doGetAuthenticationInfo"); //1.获取用户身份信息 UsernamePasswordToken uToken= (UsernamePasswordToken)token; String username=uToken.getUsername(); //2.基于用户身份查询数据库信息 SysUser sysUser= sysUserDao.findUserByUserName(username); //3.对查询结果进行封装. //3.1获取用户salt值,并将其转换为一个字节源对象 ByteSource byteSource= ByteSource.Util.bytes(sysUser.getSalt()); //3.2对用户信息进行封装返回. AuthenticationInfo info= new SimpleAuthenticationInfo( sysUser.getUsername(), //主身份 sysUser.getPassword(), //已加密的密码 byteSource,//salt对应的字节源对象 getName());//realm 的名字 return info; } }
public interface SysUserDao { /** * 根据用户名查找用户信息 * @param username * @return */ SysUser findUserByUserName(String username); }
<select id="findUserByUserName" resultType="sysUser"> select * from sys_users where username=#{username} </select>
Authorization implementation:
Process:
- The system calls the subject related method isPermitted to submit the user information to the SecurityManager based on the resource/hasRole
2) SecurityManager delegates permission detection operations to the Authorizer object
3) Authorizer delegates user information to realm.
List<String> list = sysUserDao.findUserPermissions(username);
- Realm accesses the database to obtain user authority information and encapsulate it .
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(permissions);
- Authorizer judges the user authorization information.
@RequiresPermissions("sys:user:update")
@RequestMapping("doDeleteObject") @ResponseBody public JsonResult doDeleteObject(String idStr){ sysRoleService.deleteObject(idStr); return new JsonResult(); }
@RequiresPermissions("sys:role:delete") @Override public int deleteObject(String idStr) { //1.参数合法性验证 if(StringUtils.isEmpty(idStr)) throw new ServiceException("必须选中才能删除"); //2.解析字符串 String[] ids=idStr.split(","); //3.调用数据层方法执行删除操作 int rows=sysRoleDao.deleteObject(ids); for(String id:ids){ sysRoleMenuDao.deleteObject(Integer.valueOf(id)); sysUserRoleDao.deleteObject(null, Integer.valueOf(id)); } //4.返回处理结果 if(rows==0) throw new ServiceException("数据已经不存在"); return rows; }
Realize basic authentication and permission control through Realm
public class ShiroUserRealm extends AuthorizingRealm { @Autowired private SysUserDao sysUserDao; /*** * 完成授权信息的获取以及封装. * 此方法何时调用?(执行授权检测时调用) * */ @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { System.out.println("realm.doGetAuthorizationInfo"); //1.获取登陆用户身份信息 String username= (String)principals.getPrimaryPrincipal(); //2.查找用户的权限信息 List<String> list=//sys:user:update,sys:user:view,...., sysUserDao.findUserPermissions(username); System.out.println("list="+list); Set<String> permissions=new HashSet<>(); for(String permission:list){ if(!StringUtils.isEmpty(permission)){ permissions.add(permission); } } System.out.println("set="+permissions); //3.对权限信息进行封装 SimpleAuthorizationInfo info= new SimpleAuthorizationInfo(); info.setStringPermissions(permissions); return info; }
/** * 根据用户id查找用户权限标识信息 * 例如:sys:role:view,sys:role:add * @param userId * @return */ List<String> findUserPermissions(String username);
<select id="findUserPermissions" resultType="string"> select m.permission from sys_users u join sys_user_roles ur join sys_role_menus rm join sys_menus m on u.id=ur.user_id and ur.role_id=rm.role_id and rm.menu_id=m.id where u.username=#{username} </select>
<!-- 整合Shiro 安全框架--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency>
Related table:
sys_roles
sys_users
sys_menus
sys_role_menus
sys_user_roles
CREATE TABLE `sys_roles` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL COMMENT '角色名称',
`note` varchar(500) DEFAULT NULL COMMENT '备注',
`createdTime` datetime DEFAULT NULL COMMENT '创建时间',
`modifiedTime` datetime DEFAULT NULL COMMENT '修改时间',
`createdUser` varchar(20) DEFAULT NULL COMMENT '创建用户',
`modifiedUser` varchar(20) DEFAULT NULL COMMENT '修改用户',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=45 DEFAULT CHARSET=utf8 COMMENT='角色';CREATE TABLE `sys_users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) DEFAULT NULL COMMENT '密码',
`salt` varchar(50) DEFAULT NULL COMMENT '盐 密码加密时前缀,使加密后的值不同',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`mobile` varchar(100) DEFAULT NULL COMMENT '手机号',
`valid` tinyint(4) DEFAULT NULL COMMENT '状态 0:禁用 1:正常 默认值 :1',
`createdTime` datetime DEFAULT NULL COMMENT '创建时间',
`modifiedTime` datetime DEFAULT NULL COMMENT '修改时间',
`createdUser` varchar(20) DEFAULT NULL COMMENT '创建用户',
`modifiedUser` varchar(20) DEFAULT NULL COMMENT '修改用户',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COMMENT='系统用户';CREATE TABLE `sys_menus` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL COMMENT '资源名称',
`url` varchar(200) DEFAULT NULL COMMENT '资源URL',
`type` int(11) DEFAULT NULL COMMENT '类型 1:菜单 2:按钮',
`sort` int(11) DEFAULT NULL COMMENT '排序',
`note` varchar(100) DEFAULT NULL COMMENT '备注',
`parentId` int(11) DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',
`permission` varchar(500) DEFAULT NULL COMMENT '授权(如:user:create)',
`createdTime` datetime DEFAULT NULL COMMENT '创建时间',
`modifiedTime` datetime DEFAULT NULL COMMENT '修改时间',
`createdUser` varchar(20) DEFAULT NULL COMMENT '创建用户',
`modifiedUser` varchar(20) DEFAULT NULL COMMENT '修改用户',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=141 DEFAULT CHARSET=utf8 COMMENT='资源管理';CREATE TABLE `sys_role_menus` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_id` int(11) DEFAULT NULL COMMENT '角色ID',
`menu_id` int(11) DEFAULT NULL COMMENT 'ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1218 DEFAULT CHARSET=utf8 COMMENT='角色与菜单对应关系';CREATE TABLE `sys_user_roles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL COMMENT '用户ID',
`role_id` int(11) DEFAULT NULL COMMENT '角色ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8 COMMENT='用户与角色对应关系';INSERT INTO `jt_sys`.`sys_users`(`id`, `username`, `password`, `salt`, `email`, `mobile`, `valid`, `createdTime`, `modifiedTime`, `createdUser`, `modifiedUser`) VALUES (1, 'admin', '4ebd394fbd25e495e0753a7dc9889a8e', '7adb778c-e7d3-4dd3-a3c5-5f80a158006d', '[email protected]', '13624356789', 1, NULL, '2018-01-13 02:06:45', NULL, 'admin');
INSERT INTO `jt_sys`.`sys_menus`(`id`, `name`, `url`, `type`, `sort`, `note`, `parentId`, `permission`, `createdTime`, `modifiedTime`, `createdUser`, `modifiedUser`) VALUES (45, '用户管理', 'user/listUI.do', 1, 45, NULL, 8, 'sys:user:view', '2017-07-12 15:15:59', '2017-07-21 17:36:01', 'admin', 'admin');