一、引入maven配置
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
二、建表
用户表、角色表、权限表、用户角色表、角色权限表。
用户表:
角色表:
权限表:news:* 表示有新闻的所有权限(包括增删改查),而news:add,只有新闻的新增权限。
用户角色表:用户拥有哪些角色。
角色权限表:角色拥有哪些权限。
三、自定义Realm
自定义realm主要用于用户的权限验证以及登录验证。
自定义realm继承AuthorizingRealm类,并重写doGetAuthorizationInfo和doGetAuthenticationInfo方法,doGetAuthorizationInfo方法主要校验用户的权限,即该用户拥有什么角色以及权限。doGetAuthenticationInfo用于校验登录验证,即用户的用户名或者密码是否正确。
/**
* 类名 : shiro的Realm
* 用法 :
* 创建人 : shyroke
* 时间:2018/12/12 17:29
*/
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserMapper userMapper;
@Autowired
private PermissionMapper permissionMapper;
/**
* 授权,即该用户拥有什么角色以及权限
* 步骤:根据用户名获取角色以及权限,然后设置到验证信息类并返回。
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
String userName = principalCollection.getPrimaryPrincipal().toString();
// 获取该用户的角色
List<Role> roles = userMapper.getRolesByUserName(userName);
Set<String> roleSets = new HashSet<>();
// 获取该用户的权限集
List<Permission> permissions = new ArrayList<>();
Set<String> permissoinSet = new HashSet<>();
//将List转为Set
if(roles !=null && roles.size()>0){
for(Role role:roles){
roleSets.add(role.getName());
permissions.addAll(permissionMapper.getPermissionByRoleId(role.getId()));
}
}
//将List转为Set
if(permissions!=null & permissoinSet.size()>0){
for(Permission p :permissions){
permissoinSet.add(p.getName());
}
}
info.addRoles(roleSets);
info.addStringPermissions(permissoinSet);
return info;
}
/**
* 认证,即用户账号密码是否正确
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 获取用户名和密码
String userName = (String)token.getPrincipal();
String passWord = new String((char[]) token.getCredentials());
// 根据用户名查找该用户
User user = userMapper.getUserByName(userName);
if(user == null){
throw new UnknownAccountException("用户名不存在");
}
if(!user.getPassword().equals(passWord)){
throw new IncorrectCredentialsException("密码错误");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getName(),user.getPassword(),getName());
return info;
}
}
四、shiro配置类
/**
* 类名 :shiro的核心配置类
* 用法 :
* 创建人 : shyroke
* 时间:2018/12/14 15:20
*/
@Configuration
public class ShiroConfigration {
// 设置自定义Realm
@Bean
public MyRealm myRealm(){
return new MyRealm();
}
// 权限管理,配置主要是Realm的管理认证
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(myRealm());
return manager;
}
/**
* 设置过滤条件和跳转条件
* anon 不生效的原因:1、map的类型必须是LinkedHashMap 2、anon必须定义在authc之前
*
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
// 设置登录跳转
factoryBean.setLoginUrl("/admin");
factoryBean.setSuccessUrl("/admin/index");
//必须为LinkedHashMap 否则anon不生效
Map<String,String> map = new LinkedHashMap<>();
//退出
map.put("/admin/logout","logout");
//登录页面和登录验证不要拦截
map.put("/admin/login.html","anon");
map.put("/admin/tologin","anon");
//设置需要过滤的链接
map.put("/admin/**","authc");
factoryBean.setFilterChainDefinitionMap(map);
return factoryBean;
}
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
* 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可实现此功能
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 开启aop注解支持
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
五、调用
代码如下,当调用Subject.login方法后,会调用自定义Realm的doGetAuthenticationInfo方法校验用户名密码是否正确,如果不正确则抛出对应异常,controller层捕获并处理异常。
AuthenticationToken usernamePasswordToken = new UsernamePasswordToken(user.getName(),user.getPassword());
Subject subject = SecurityUtils.getSubject();
try {
subject.login(usernamePasswordToken);
}catch (UnknownAccountException ex) {
logger.info("用户名错误!");
return R.error("用户名错误!");
} catch (IncorrectCredentialsException ex) {
logger.info("密码错误!");
return R.error("密码错误!");
} catch (AuthenticationException ex) {
logger.info(ex.getMessage());
return R.error("系统错误,请查看日志");
} catch (Exception ex) {
logger.info(ex.getMessage());
return R.error("系统错误,请查看日志");
}