今日是進行動態菜單生成與權限管理的完善
# 一.session處理javascript
## 1.1 登陸成功後主體爲用戶
> 之前登陸成功,傳的是username,如今傳==主體Employee對象==
```
//身份認證java
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { ... SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(loginUser,password,salt,getName()); return authenticationInfo; }
```web
## 1.2 UserContext
> session是從subject獲取ajax
> 存在shiro的session中後,HttpSession也會有值```spring
public class UserContext { public static final String USER_IN_SESSION ="loginUser"; //把登陸成功的用戶放到session中 public static void setUser(Employee loginUser){ Subject subject = SecurityUtils.getSubject(); //表明登陸成功,把當前登陸用戶放到Session中去(shiro的session) //1.拿到session Session session = subject.getSession(); //2.把當前登陸成功的用戶放到session中去 session.setAttribute(USER_IN_SESSION, loginUser); } //獲取到當前登陸用戶 public static Employee getUser(){ Subject subject = SecurityUtils.getSubject(); Session session = subject.getSession(); Employee employee = (Employee) session.getAttribute(USER_IN_SESSION); return employee; } }
```數據庫
# 二.受權管理apache
## 2.1 FilterChainDefinitionMapFactory
> 保存全部權限過濾的數據都是從數據庫中獲取```json
@Autowired private IPermissionService permissionService; public Map<String,String> createFilterChainDefinitionMap(){ ... //拿到全部權限 List<Permission> perms = permissionService.findAll(); //設置相應的權限 perms.forEach(p -> { filterChainDefinitionMap.put(p.getUrl(), "perms["+p.getSn()+"]"); }); filterChainDefinitionMap.put("/**", "authc"); return filterChainDefinitionMap; }
```session
## 2.2 獲取受權
> 受權部分的數據也是從數據庫中得到的
>> 應該拿到當前登陸用戶的全部權限app
### PermissionRepository
> JPQL關聯原則: 1.不寫on 2.關聯對象的別名.屬性
```
//根據用戶拿到他對應的全部權限 @Query("select distinct p.sn from Employee e join e.roles r join r.permissions p where e.id = ?1") Set<String> findPermsByUser(Long userId);
```
### PermissionService(調用略...)
### JpaRealm
> 拿到當前登陸用戶,再獲取它的權限
```
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { Employee loginUser = UserContext.getUser(); //根據當前用戶拿到對應的權限 Set<String> perms = permissionService.findPermsByUser(loginUser.getId()); //準備並返回AuthorizationInfo這個對象 SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.setStringPermissions(perms); return authorizationInfo; }
```
## 2.3 Ajax請求的權限處理
> shiro處理沒有權限是跳轉頁面,而咱們若是是ajax請求,咱們但願是返回json數據
> ajax請求會有一個請求頭:X-Requested-With: XMLHttpRequest
> 須要自定義一個shiro的權限過濾器
### 2.3.1 自定義權限過濾器```
public class AisellPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter { protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException { Subject subject = getSubject(request, response); // If the subject isn't identified, redirect to login URL if (subject.getPrincipal() == null) { saveRequestAndRedirectToLogin(request, response); } else { //一.拿到請求頭 HttpServletRequest req = (HttpServletRequest)request; // 拿到響應頭 HttpServletResponse resp = (HttpServletResponse)response; //設置響應頭 resp.setContentType("application/json;charset=UTF-8"); String xr = req.getHeader("X-Requested-With"); //二.判斷這個請求頭是不是Ajax請求 if(xr!=null && "XMLHttpRequest".equals(xr)){ //返回一個json {"success":false,"msg":"權限不足,請充值!"} resp.getWriter().print("{\"success\":false,\"msg\":\"你的權限不足,請充值!\"}"); }else { //普通請求:拿到沒有權限的跳轉路徑,進行跳轉 String unauthorizedUrl = getUnauthorizedUrl(); if (StringUtils.hasText(unauthorizedUrl)) { WebUtils.issueRedirect(request, response, unauthorizedUrl); } else { WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED); } } } return false; } }
爲保留原有的功能,幾乎全部代碼都是拷的父類,只在必要的地方作修改
```
### 2.3.2 applicationContext-shiro.xml
> 配置權限過濾器
>> entry key="aisellPerms":肯定權限過濾器的名稱---與權限的前綴一致key[權限]
<!-- 真正實現權限的過濾器 它的id名稱和web.xml中的過濾器名稱同樣 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 設置權限過濾器 --> <property name="filters"> <map> <entry key="aisellPerms" value-ref="aisellPermissionsAuthorizationFilter"/> </map> </property> </bean> <!-- 配置自定義shiro過濾器 --> <bean id="aisellPermissionsAuthorizationFilter" class="cn.itsource.aisell.shiro.AisellPermissionsAuthorizationFilter" />
```
### 2.3.3 修改過濾器配置
```
@Autowired private IPermissionService permissionService; public Map<String,String> createFilterChainDefinitionMap(){ ... //拿到全部權限 List<Permission> perms = permissionService.findAll(); //設置相應的權限 perms.forEach(p -> { filterChainDefinitionMap.put(p.getUrl(), "aisellPerms["+p.getSn()+"]"); }); ... }
# 三.菜單管理
> 員工 -> 角色 -> 權限 -> ==菜單==## 3.1 Menu
> 菜單domain的自關連配置
>> 須要配置雙向,可是不能讓JPA去管理一對多(咱們本身管理:@Transient)
>>> 雙向生成JSON會產生死循環,須要一邊進行忽略:@JsonIgnore
```
//讓它再也不生成JSON @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "parent_id") @JsonIgnore private Menu parent; // 臨時屬性 -> 這個字段JPA就無論它了 @Transient private List<Menu> children = new ArrayList<>();
```
注 : 要保證menu正確顯示
//在menu類中應該配有getText,一保證得到對應的文本信息 public String getText() { return name; }
## 3.2 MenuRepository
```
public interface MenuRepository extends BaseRepository<Menu,Long>{ @Query("select distinct m from Employee e join e.roles r join r.permissions p join p.menu m where e.id = ?1") List<Menu> findByUser(Long userId); }
```
## 3.2 MenuService
> 根據設計只能經過員工找到子菜單
>> 須要經過子菜單拿到父菜單
>>> 判斷這個父菜單是否已經存到集合中
>>>> 若是這個菜單單沒有存起來,放到集合中
> 把當前這個子菜單放到父菜單中去
```
@Override public List<Menu> findLoginMenu() { //1.準備一個裝父菜單的容器 List<Menu> parentMenus = new ArrayList<>(); //2.拿到當前登陸用戶的全部子菜單 Employee loginUser = UserContext.getUser(); List<Menu> children = menuRepository.findByUser(loginUser.getId()); //3.遍歷子菜單,設置它們的關係 for (Menu child : children) { //3.1 根據子菜單拿到它對應的父菜單 Menu parent = child.getParent(); //3.2 判斷這個父菜單是否在容器中 if(!parentMenus.contains(parent)){ //3.3 若是不在,把父菜單放進去 parentMenus.add(parent); } //3.4 爲這個父菜單添加對應的子菜單 parent.getChildren().add(child); } return parentMenus; }
## 3.3 UtilController中返回值
@Autowired private IMenuService menuService; @RequestMapping("/loginUserMenu") @ResponseBody public List<Menu> loginUserMenu(){ return menuService.findLoginMenu(); }
## 3.4 main.jsp修改路徑
$('#menuTree').tree({ url:'/util/loginUserMenu', ...
# shiro:hasPermission
> 沒有這個權限,就不展現對應的按鍵
```
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> ... <shiro:hasPermission name="employee:delete"> <a href="javascript:;" data-method="delete" class="easyui-linkbutton" iconCls="icon-remove" plain="true">刪除</a> </shiro:hasPermission>
```