数据库相关信息
要想对角色权限进行管理的话,至少需要五张表,无论是一个人拥有一个角色,或者多个角色,都需要五张表
分别是
用户表,角色表,用户角色表,权限表,角色权限表
具体表如下,这里偷懒,不加外键了
ShiroConfig
重中之重的 shiro配置如下:
@Configurationpublic class ShiroConfig {/*** ShiroFilterFactoryBean 处理拦截资源文件问题。* 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在* 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager** Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过* 3、部分过滤器可指定参数,如perms,roles**/@Bean(name = "shiroFilterFactoryBean")public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();// 必须设置 SecurityManagershiroFilterFactoryBean.setSecurityManager(securityManager);// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面shiroFilterFactoryBean.setLoginUrl("/login.html");shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth.html");// 拦截器.Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();// 配置不会被拦截的链接 顺序判断filterChainDefinitionMap.put("/static/**", "anon");filterChainDefinitionMap.put("/user/login", "anon");// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了filterChainDefinitionMap.put("/logout", "logout");// 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 :这是一个坑呢,一不小心代码就不好使了;// ① authc:所有url都必须认证通过才可以访问; ② anon:所有url都都可以匿名访问filterChainDefinitionMap.put("/**", "authc");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}@Bean(name = "securityManager")public DefaultWebSecurityManager securityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 设置realm.securityManager.setRealm(myRealm());return securityManager;}/*** 身份认证realm; (这个需要自己写,账号密码校验;权限等)** @return*/@Beanpublic MyRealm myRealm() {MyRealm myRealm = new MyRealm();return myRealm;}/*** Shiro生命周期处理器* @return*/@Beanpublic LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/*** 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证* 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能* @return*/@Bean@DependsOn({ "lifecycleBeanPostProcessor" })public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();advisorAutoProxyCreator.setProxyTargetClass(true);return advisorAutoProxyCreator;}@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());return authorizationAttributeSourceAdvisor;}}
编写认证和授权逻辑
public class MyRealm extends AuthorizingRealm {@Autowiredprivate UserDao userDao;@Autowiredprivate RoleDao roleDao;@Autowiredprivate PermissionDao permissionDao;/*** 授权*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {String userName=(String) SecurityUtils.getSubject().getPrincipal();SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();Set<String> roles=new HashSet<String>();List<Role> rolesByUserName = roleDao.getRolesByUserName(userName);rolesByUserName.stream().forEach((role) -> {roles.add(role.getRoleName());});List<Permission> permissionsByUserName = permissionDao.getPermissionsByUserName(userName);permissionsByUserName.stream().forEach((permission) ->{info.addStringPermission(permission.getPermissionName());});info.setRoles(roles);return info;}/*** 认证*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("token.getPrincipal:" + token.getPrincipal());System.out.println("token.getCredentials:" + token.getCredentials());String userName = token.getPrincipal().toString();User user = userDao.getUserByUserName(userName);if (user != null) {//说明用户存在// Object principal, Object credentials, String realmNameAuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());return authcInfo;} else {//否则认证失败return null;}}}
UserController
具体的相关接口如下
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/login")
public Map<String, Object> login(@Valid User user, BindingResult bindingResult, HttpSession session) {
Map<String, Object> map = new HashMap<String, Object>();
// 1、JSR303
if (bindingResult.hasErrors()) {
map.put("success", false);
map.put("errorInfo", bindingResult.getFieldError().getDefaultMessage());
return map;
}
// 2、Shiro
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
subject.login(token);
map.put("success", true);
return map;
} catch (Exception e) {
e.printStackTrace();
map.put("success", false);
map.put("errorInfo", "用户名或者密码错误!");
return map;
}
}
@RequiresPermissions({"select"}) //没有的话 AuthorizationException
@PostMapping("/select")
public Map<String, Object> selectPermission() {
System.out.println("select");
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("msg", "当前角色有查看的权力");
return map;
}
@RequiresPermissions({"insert"}) //没有的话 AuthorizationException
@PostMapping("/insert")
public Map<String, Object> insertPermission() {
System.out.println("insert");
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("msg", "当前角色有增加的权力");
return map;
}
@RequiresPermissions({"update"}) //没有的话 AuthorizationException
@PostMapping("/update")
public Map<String, Object> updatePermission() {
System.out.println("update");
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("msg", "当前角色有更新的权力");
return map;
}
@RequiresPermissions({"delete"}) //没有的话 AuthorizationException
@PostMapping("/delete")
public Map<String, Object> deletePermission() {
System.out.println("delete");
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("msg", "当前角色有删除的权力");
return map;
}
@RequiresRoles({"vip"}) //没有的话 AuthorizationException
@PostMapping("/vip")
public Map<String, Object> vipRole() {
System.out.println("vip");
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("msg", "当前用户具有 vip 角色");
return map;
}
@RequiresRoles({"ip"}) //没有的话 AuthorizationException
@PostMapping("/ip")
public Map<String, Object> ipRole() {
System.out.println("ip");
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("msg", "当前用户具有 ip 角色");
return map;
}
@RequiresRoles({"p"}) //没有的话 AuthorizationException
@PostMapping("/p")
public Map<String, Object> pRole() {
System.out.println("vip");
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("msg", "当前用户具有 p 角色");
return map;
}
}
对AuthorizationException进行处理
如果没有权限访问资源,控制台会报错,而且会直接返给前台错误,看着十分不舒服。
于是对错误进行了一些处理
@ControllerAdvice
public class NoPermissionException {
@ResponseBody
@ExceptionHandler(UnauthorizedException.class)
public String handleShiroException(Exception ex) {
return "无权限";
}
@ResponseBody
@ExceptionHandler(AuthorizationException.class)
public String AuthorizationException(Exception ex) {
return "权限认证失败";
}
}
