SpringBoot集成Shiro
Shiro是一个安全框架,控制登陆,角色权限管理(身份认证、授权、回话管理、加密)
Shiro不会去维护用户,维护权限;这些需要通过realm让开发人员自己注入
1、在pom.xml中引入shiro的jar包
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.4.0</version> </dependency>
2、在src\main\resources下创建ehcache-shiro.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <ehcache updateCheck="false" name="cacheManagerConfigFile"> <defaultCache maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"/> <cache name="shiro-activeSessionCache" eternal="false" maxElementsInMemory="10000" overflowToDisk="false" timeToIdleSeconds="0" timeToLiveSeconds="0"/> </ehcache>
3、创建User类
package com.cppdy.entity; public class User { private int id; private String username; private String password; private int roleid; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public int getRoleid() { return roleid; } public void setRoleid(int roleid) { this.roleid = roleid; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
4、创建Role类
package com.cppdy.entity; public class Role { private int id; private String rolename; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getRolename() { return rolename; } public void setRolename(String rolename) { this.rolename = rolename; } }
5、创建Permission类
package com.cppdy.entity; public class Permission { private int id; private String pername; private int roleid; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getPername() { return pername; } public void setPername(String pername) { this.pername = pername; } public int getRoleid() { return roleid; } public void setRoleid(int roleid) { this.roleid = roleid; } }
6、创建UserMapper接口
package com.cppdy.mapper; import org.apache.ibatis.annotations.Mapper; import com.baomidou.mybatisplus.mapper.BaseMapper; import com.cppdy.entity.User; @Mapper public interface UserMapper extends BaseMapper<User>{ }
7、创建RoleMapper接口
package com.cppdy.mapper; import org.apache.ibatis.annotations.Mapper; import com.baomidou.mybatisplus.mapper.BaseMapper; import com.cppdy.entity.Role; @Mapper public interface RoleMapper extends BaseMapper<Role>{ }
8、创建PermissionMapper接口
package com.cppdy.mapper; import org.apache.ibatis.annotations.Mapper; import com.baomidou.mybatisplus.mapper.BaseMapper; import com.cppdy.entity.Permission; @Mapper public interface PermissionMapper extends BaseMapper<Permission>{ }
9、创建UserService接口
package com.cppdy.service; import com.cppdy.entity.User; public interface UserService { public void update(String username, int id); public User selectUserByUsername(String username); }
10、创建UserServiceImpl接口实现类
package com.cppdy.service.impl; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.baomidou.mybatisplus.mapper.Wrapper; import com.cppdy.entity.User; import com.cppdy.mapper.UserMapper; import com.cppdy.service.UserService; @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; // 开启事务管理 @Transactional public void update(String username, int id) { User user = userMapper.selectById(id); user.setUsername(username); // 更新一条数据 userMapper.updateById(user); } @Override public User selectUserByUsername(String username) { Wrapper<User> wrapper = new EntityWrapper<>(); wrapper.eq("username", username); List<User> list = userMapper.selectList(wrapper); if(list.size()>0) { return list.get(0); } return null; } }
11、创建UserRealm类
package com.cppdy.realm; import java.util.ArrayList; import java.util.List; 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.springframework.beans.factory.annotation.Autowired; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.baomidou.mybatisplus.mapper.Wrapper; import com.cppdy.entity.Permission; import com.cppdy.entity.Role; import com.cppdy.entity.User; import com.cppdy.mapper.PermissionMapper; import com.cppdy.mapper.RoleMapper; import com.cppdy.service.UserService; public class UserRealm extends AuthorizingRealm { @Autowired private UserService userService; @Autowired private RoleMapper roleMapper; @Autowired private PermissionMapper permissionMapper; // 控制角色权限 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); String username = (String) principals.getPrimaryPrincipal(); // 将当前用户的角色和权限查询进来 User user = userService.selectUserByUsername(username); Role role = roleMapper.selectById(user.getRoleid()); info.addRole(role.getRolename()); Wrapper<Permission> wrapper = new EntityWrapper<>(); wrapper.eq("roleid", role.getId()); List<Permission> selectList = permissionMapper.selectList(wrapper); ArrayList<String> perList = new ArrayList<String>(); selectList.forEach(per -> { perList.add(per.getPername()); }); info.addStringPermissions(perList); return info; } // 控制登陆 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { AuthenticationInfo info = null; String username = (String) token.getPrincipal(); User user = userService.selectUserByUsername(username); if (user != null) { info = new SimpleAuthenticationInfo(username, user.getPassword(), "cppdy"); } return info; } }
12、创建ShiroConfiguration配置类
package com.cppdy.config; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.Filter; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator; import org.apache.shiro.session.mgt.eis.MemorySessionDAO; 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.apache.shiro.web.servlet.SimpleCookie; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.cppdy.realm.UserRealm; @Configuration public class ShiroConfiguration { @Bean public ShiroFilterFactoryBean shiroFilter(org.apache.shiro.mgt.SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean(); //必须设置SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //拦截器 Map<String,String> filterChainDefinitionMap=new LinkedHashMap<String,String>(); //配置静态资源允许访问 filterChainDefinitionMap.put("/user/login", "anon"); filterChainDefinitionMap.put("/user/loginAction", "anon"); //authc:所有url都必须认证通过才可以访问;anon:所有url都可以匿名访问 filterChainDefinitionMap.put("/**", "authc"); //如果不设置默认会自动寻找web工程跟目标下的/login.jsp页面 shiroFilterFactoryBean.setLoginUrl("/user/login"); //未授权界面 // shiroFilterFactoryBean.setUnauthorizedUrl("/403"); Map<String,Filter> filters=new HashMap<String,Filter>(); shiroFilterFactoryBean.setFilters(filters); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public EhCacheManager getEhCacheManager() { EhCacheManager em=new EhCacheManager(); em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml"); return em; } //开启Controller中的shiro注解 @Bean public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator daap=new DefaultAdvisorAutoProxyCreator(); daap.setProxyTargetClass(true); return daap; } @Bean public DefaultWebSessionManager getDefaultWebSessionManager() { DefaultWebSessionManager defaultWebSessionManager=new DefaultWebSessionManager(); defaultWebSessionManager.setSessionDAO(getMemorySessionDAO()); defaultWebSessionManager.setGlobalSessionTimeout(4200000); defaultWebSessionManager.setSessionValidationSchedulerEnabled(true); defaultWebSessionManager.setSessionIdCookieEnabled(true); defaultWebSessionManager.setSessionIdCookie(getSimpleCookie()); return defaultWebSessionManager; } @Bean public MemorySessionDAO getMemorySessionDAO() { MemorySessionDAO memorySessionDAO=new MemorySessionDAO(); memorySessionDAO.setSessionIdGenerator(javaUuidSessionIdGenerator()); return memorySessionDAO; } @Bean public JavaUuidSessionIdGenerator javaUuidSessionIdGenerator() { return new JavaUuidSessionIdGenerator(); } /** * session自定义cookie名 * @return */ @Bean public SimpleCookie getSimpleCookie() { SimpleCookie simpleCookie=new SimpleCookie(); simpleCookie.setName("security.session"); simpleCookie.setPath("/"); return simpleCookie; } @Bean public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean(name="securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm) { DefaultWebSecurityManager dwsm=new DefaultWebSecurityManager(); dwsm.setRealm(userRealm); //用户授权/认证信息Cache,采用EhCache缓存 dwsm.setCacheManager(getEhCacheManager()); dwsm.setSessionManager(getDefaultWebSessionManager()); return dwsm; } @Bean public UserRealm userRealm(EhCacheManager cacheManager) { UserRealm userRealm=new UserRealm(); userRealm.setCacheManager(cacheManager); return userRealm; } /** * 开启shiro注解支持 * @param userRealm * @return */ @Bean public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(UserRealm userRealm) { AuthorizationAttributeSourceAdvisor aasa=new AuthorizationAttributeSourceAdvisor(); aasa.setSecurityManager(getDefaultWebSecurityManager(userRealm)); return aasa; } }
13、创建UserController类
package com.cppdy.controller; import org.apache.ibatis.session.RowBounds; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.crypto.hash.Md5Hash; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.baomidou.mybatisplus.mapper.Wrapper; import com.cppdy.entity.User; import com.cppdy.mapper.UserMapper; @RestController @RequestMapping("user") public class UserController { @Autowired private UserMapper userMapper; @RequestMapping("getUserById") public Object getUserById(int id) { return userMapper.selectById(id); } @RequiresPermissions("sys:delete") @RequestMapping("deleteUserById") public Object deleteUserById(int id) { return userMapper.deleteById(id); } @RequiresRoles("admin") @RequestMapping("getUser") public Object getUser() { // 适配器 Wrapper<User> wrapper = new EntityWrapper<>(); wrapper.like("username", "测试"); // 倒序 wrapper.orderBy("id", false); return userMapper.selectList(wrapper); } @RequestMapping("selectPage") public Object selectPage(int pageNum, int pageSize) { // 适配器 Wrapper<User> wrapper = new EntityWrapper<>(); RowBounds rowBounds = new RowBounds((pageNum - 1) * pageSize, pageSize); return userMapper.selectPage(rowBounds, wrapper); } @RequestMapping("login") public String login() { return "loginPage"; } @RequestMapping("loginAction") public String loginAction(String username,String password) { Subject subject = SecurityUtils.getSubject(); String md5 = new Md5Hash(password, "cppdy").toString(); AuthenticationToken token = new UsernamePasswordToken(username, md5); try { // 如果执行subject.login抛出异常,则证明登陆成功 subject.login(token); return "login success"; } catch (AuthenticationException e) { // 有异常则证明登陆错误 e.printStackTrace(); return "login failed"; } } }
14、创建表(user、role、permission),并添加测试数据(password为:123456;md5加密后的password为:9faea48dae4030f38bcd1ae6a4f7fc01)
15、访问loginAction方法进行登录,再分别调用getUser和deleteUserById方法测试角色权限控制