shiro框架是apache旗下的一个开源安全框架,他将软件系统的安全认证相关功能抽取出来,实现用户身份认证,权限授权,加密,会话管理等功能,组成了一个通用的安全认证框架。
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.5.2version>
dependency>
@Configuration
注解import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringShiroConfig {
/**配置shiro中的核心对象:安全管理器*/
@Bean
public SecurityManager securityManager(Realm realm, CacheManager shiroCacheManager, SessionManager sessionManager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
return securityManager;
}
/**配置ShiroFilterFactoryBean对象*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager) {
ShiroFilterFactoryBean sfBean = new ShiroFilterFactoryBean();
// 设置安全管理器
sfBean.setSecurityManager(securityManager);
// 设置登录页面url
sfBean.setLoginUrl("/doLoginUI");
// 设置过滤规则,定义map指定请求过滤规则
LinkedHashMap<String,String> map=
new LinkedHashMap<>();
//静态资源允许匿名访问:"anon"
map.put("/bower_components/**","anon");
map.put("/build/**","anon");
map.put("/dist/**","anon");
map.put("/plugins/**","anon");
map.put("/user/doLogin","anon");
map.put("/doLogout","logout");// logout由shiro提供
//除了匿名访问的资源,其它都要认证("authc")后访问
map.put("/**","authc");
sfBean.setFilterChainDefinitionMap(map);
return sfBean;
}
}
@RequestMapping("doLogin")
public JsonResult doLogin(String username, String password) {
// 获取subject对象
Subject subject = SecurityUtils.getSubject();
// 把用户信息封装进token中
UsernamePasswordToken token = new UsernamePasswordToken();
token.setUsername(username);
token.setPassword(password.toCharArray());
subject.login(token);
return new JsonResult("login OK");
}
业务的实现要在Realm模型对象中进行实现,要继承AuthorizingRealm
并重写相关方法。
setCredentialsMatcher
)方法doGetAuthenticationInfo
)方法@Service
public class ShiroUserRealm extends AuthorizingRealm {
@Autowired
private SysUserDao sysUserDao;
/**
* 设置凭证匹配器(与用户添加操作使用相同的加密算法)
*/
@Override
public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
//构建凭证匹配对象
HashedCredentialsMatcher cMatcher=
new HashedCredentialsMatcher();
//设置加密算法
cMatcher.setHashAlgorithmName("MD5");
//设置加密次数
cMatcher.setHashIterations(1);
super.setCredentialsMatcher(cMatcher);
}
/**
* 通过此方法完成认证数据的获取及封装,系统
* 底层会将认证数据传递认证管理器,由认证
* 管理器完成认证操作。
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//1.获取用户名(用户页面输入)
UsernamePasswordToken upToken = (UsernamePasswordToken)token;
String username=upToken.getUsername();
//2.基于用户名查询用户信息
SysUser user = sysUserDao.findUserByUserName(username);
//3.判定用户是否存在
if (user==null)
throw new UnknownAccountException();
//4.封装用户信息
ByteSource credentialsSalt = ByteSource.Util.bytes(user.getSalt());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
user,//principal (身份)
user.getPassword(),//hashedCredentials
credentialsSalt, //credentialsSalt
getName());//realName
//5.返回封装结果
return info;//返回值会传递给认证管理器(后续认证管理器会通过此信息完成认证操作)
}
}
@Bean
public SecurityManager securityManager(Realm realm) {
DefaultWebSecurityManager sManager = new DefaultWebSecurityManager();
sManager.setRealm(realm);
return sManager;
}
Spring Boot可以不配置
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
Spring Boot可以不配置
@DependsOn("lifecycleBeanPostProcessor")
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
doGetAuthorizationInfo
方法@Service
public class ShiroUserRealm extends AuthorizingRealm {
@Autowired
private SysUserDao sysUserDao;
@Autowired
private SysUserRoleDao sysUserRoleDao;
@Autowired
private SysRoleMenuDao sysRoleMenuDao;
@Autowired
private SysMenusDao sysMenuDao;
/**负责获取登录用户权限信息并进行封装*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//1.获取登陆用户身份信息
SysUser user=(SysUser)principals.getPrimaryPrincipal();
//2.基于用户id获取角色id
List<Integer> roleIds=
sysUserRoleDao.findRoleIdsByUserId(user.getId());
if(roleIds==null||roleIds.size()==0)
throw new AuthorizationException();
//3.基于角色id获取对应的菜单id
List<Integer> menuIds=
sysRoleMenuDao.findMenuIdsByRoleIds(roleIds.toArray(new Integer[] {}));
if(menuIds==null||menuIds.size()==0)
throw new AuthorizationException();
//4.基于菜单id获取授权标识
List<String> permissions=
sysMenuDao.findPermissions(menuIds.toArray(new Integer[] {}));
if(permissions==null||permissions.size()==0)
throw new AuthorizationException();
//5.对用户权限信息进行封装
Set<String> stringPermissions=new HashSet<>();
for(String per:permissions) {
if(!StringUtils.isEmpty(per)) {
stringPermissions.add(per);
}
}
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.setStringPermissions(stringPermissions);
return info;//此对象会返回给授权管理器
}
}
在需要授权访问的业务成方法上,添加@RequiresPermissions(“需要的权限”)
注解
/**
* 禁用和启用操作
* shiro框架通过这个注解定义切入点,在这里表示
* 访问此方法需要进行授权,需要具备这个注解中的权限标识
* 1、需要系统基于登录用户获取用户权限{"sys:user:update", "sys:user:view",...}
* 2、当用户包含注解中定义的权限表示,就标识用户拥有访问这个方法的权限
* 3=拥有权限则可以由shiro框架进行授权访问
*/
@RequiresPermissions("sys:user:update")
@RequiredLog(operation = "禁用启用")
@Override
public int validById(Long id, Integer valid) {
if (id == null || id < 1)
throw new IllegalArgumentException("id值无效");
System.out.println(valid);
if (valid != 0 && valid != 1)
throw new IllegalArgumentException("状态值不正确");
int row = sysUserDao.validById(id, valid, "admin");// admin是将来的登录用户
if (row == 0)
throw new ServiceException("记录可能不存在!");
return row;
}
@Bean
public CacheManager shiroCacheManager(){
return new MemoryConstrainedCacheManager();
}
@Bean
public SecurityManager securityManager( Realm realm, CacheManager cacheManager) {
DefaultWebSecurityManager sManager = new DefaultWebSecurityManager();
sManager.setRealm(realm);
sManager.setCacheManager(cacheManager);
return sManager;
}
isRememberMe:$("#rememberId").prop("checked")
@RequestMapping("doLogin")
public JsonResult doLogin(String username, String password, Boolean isRememberMe) {
// 获取subject对象
Subject subject = SecurityUtils.getSubject();
// 把用户信息封装进token中
UsernamePasswordToken token = new UsernamePasswordToken();
token.setUsername(username);
token.setPassword(password.toCharArray());
if (isRememberMe != null) {
token.setRememberMe(isRememberMe);
}
subject.login(token);
return new JsonResult("login OK");
}
@Bean
public RememberMeManager rememberMeManager() {
CookieRememberMeManager cManager = new CookieRememberMeManager();
SimpleCookie cookie = new SimpleCookie("remeberMe");
cookie.setMaxAge(7*24*60*60);
cManager.setCookie(cookie);
return cManager;
}
@Bean
public SecurityManager securityManager(Realm realm, CacheManager shiroCacheManager, RememberMeManager rememberManager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
securityManager.setCacheManager(shiroCacheManager);
securityManager.setRememberMeManager(rememberManager);
return securityManager;
}
map.put("/**","authc");
修改为map.put("/**","user");
@Bean
public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager) {
ShiroFilterFactoryBean sfBean = new ShiroFilterFactoryBean();
// 设置安全管理器
sfBean.setSecurityManager(securityManager);
// 设置登录页面url
sfBean.setLoginUrl("/doLoginUI");
// 设置过滤规则,定义map指定请求过滤规则
LinkedHashMap<String,String> map=
new LinkedHashMap<>();
//静态资源允许匿名访问:"anon"
map.put("/bower_components/**","anon");
map.put("/build/**","anon");
map.put("/dist/**","anon");
map.put("/plugins/**","anon");
map.put("/user/doLogin","anon");
map.put("/doLogout","logout");// logout由shiro提供
//除了匿名访问的资源,其它都要认证("authc")后访问
map.put("/**","user");// 设置记住我后要访问user
sfBean.setFilterChainDefinitionMap(map);
return sfBean;
}
使用shiro框架完成认证操作后,用户登录成功会将用户的信息写入到会话对象中,默认时长为30分钟。
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager sManager = new DefaultWebSessionManager();
sManager.setGlobalSessionTimeout(60*60*24);
return sManager;
}
@Bean
public SecurityManager securityManager(Realm realm, CacheManager shiroCacheManager,
RememberMeManager rememberManager, SessionManager sessionManager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
securityManager.setCacheManager(shiroCacheManager);
securityManager.setRememberMeManager(rememberManager);
securityManager.setSessionManager(sessionManager);
return securityManager;
}