天天看點

SSM練習第五天

文章目錄

  • ​​第一章:授權操作​​
  • ​​第一節:經典權限5張表的關系分析​​
  • ​​1、資料庫模型關系圖​​
  • ​​2、使用者角色關系表​​
  • ​​3、角色權限關系表​​
  • ​​4、修改使用者SysUser實體​​
  • ​​5、修改角色Role實體​​
  • ​​6、修改權限Permission實體​​
  • ​​第二節:檢視使用者的角色詳情功能​​
  • ​​1、頁面入口​​
  • ​​2、編寫Controller​​
  • ​​3、編寫Service​​
  • ​​4、編寫Dao​​
  • ​​編寫userDao​​
  • ​​編寫RoleDao(user下有role)​​
  • ​​編寫PermissionDao(Role下有permission)​​
  • ​​5、修改前端user-show.jsp​​
  • ​​第三節:為使用者配置設定角色-角色清單資料回顯​​
  • ​​1、頁面入口​​
  • ​​2、編寫Controller​​
  • ​​3、頁面回顯資料(user-role-add.jsp)​​
  • ​​第四節:為使用者配置設定角色-更新關系到使用者角色中間表​​
  • ​​1、頁面入口​​
  • ​​2、編寫Controller​​
  • ​​3、編寫Service​​
  • ​​4、編寫Dao​​
  • ​​第五節:為角色添權重限資料回顯(和user添加role雷同)​​
  • ​​1、頁面入口​​
  • ​​2、編寫Controller​​
  • ​​3、編寫Service​​
  • ​​4、編寫Dao​​
  • ​​5、前端頁面(role-permission-add.jsp)​​
  • ​​第六節:為角色添權重限-實際儲存即往中間表插入記錄(和user添加role雷同)​​
  • ​​1、頁面入口​​
  • ​​2、編寫Controller​​
  • ​​3、編寫service​​
  • ​​4、編寫dao​​
  • ​​第七節:為使用者設定真正的角色​​
  • ​​第二章:授權後的安全控制​​
  • ​​第一節:在JSP頁面控制菜單權限(不顯示UI)​​
  • ​​1、spring-security.xml配置檔案​​
  • ​​第二節:在伺服器端控制權限(攔截請求 防止使用者直接輸入url通路)​​
  • ​​1、修改spring-mvc.xml配置​​
  • ​​2、在需要進行控制的Controller上添加@Secured注解​​
  • ​​3、更換預設403 forbidden界面​​
  • ​​第三章:系統日志功能​​
  • ​​第一節:AOP記錄日志​​
  • ​​1、日志表sys_log和實體Log​​
  • ​​2、springmvc.xml配置檔案中開啟aop的自動代理​​
  • ​​3、在web.xml中配置監聽request對象的監聽器​​
  • ​​4、編寫切面類(切面類内部有增強)​​
  • ​​5、Service代碼實作​​
  • ​​6、Dao代碼實作​​
  • ​​第二節 查詢日志​​
  • ​​1、頁面入口​​
  • ​​2、編寫Controller​​
  • ​​3、編寫service​​
  • ​​4、編寫dao​​
  • ​​5、修改domain​​
  • ​​6、修改頁面​​

第一章:授權操作

第一節:經典權限5張表的關系分析

1、資料庫模型關系圖

SSM練習第五天

2、使用者角色關系表

多對多的關系:就是兩個一對多

一對多:在一的方向添加一個集合屬性即可

-- 使用者角色關系表  多對多 引入中間表
CREATE TABLE sys_user_role(
    userId number,
    roleId number,
    PRIMARY KEY(userId,roleId), --聯合主鍵:兩個不能同時一樣
    FOREIGN KEY (userId) REFERENCES sys_USER(id),
    FOREIGN KEY (roleId) REFERENCES sys_role(id) 
)      

3、角色權限關系表

CREATE TABLE sys_role_permission(
    permissionId number,
    roleId number,
    PRIMARY KEY(permissionId,roleId),
    FOREIGN KEY (permissionId) REFERENCES sys_permission(id),
    FOREIGN KEY (roleId) REFERENCES sys_role(id) 
)      

4、修改使用者SysUser實體

@Data
public class SysUser {
    private Long id;
    private String username;
    private String email;
    private String password;
    private String phoneNum;
    private int status;

    //一個使用者對應多個角色
    private List<Role> roles = new ArrayList<>();//new出來 防止空指針
}      

5、修改角色Role實體

@Data
public class Role {
    private Integer id;
    private String roleName;
    private String roleDesc;

    //一個角色被多個使用者所擁有
    private List<SysUser> users = new ArrayList<>();
    //一個角色擁有多個權限
    private List<Permission> permissions = new ArrayList<>();
}      

6、修改權限Permission實體

@Data
public class Permission {
    private Integer id;
    private String permissionName;
    private String url;
    private Integer pid;//寫簡單的pid 不能寫Permission對象 随便想想也是死循環

    //一個權限被多個角色擁有
    private List<Role> roles=new ArrayList<>();
}      

第二節:檢視使用者的角色詳情功能

1、頁面入口

SSM練習第五天
<a href="${pageContext.request.contextPath}/user/details?userId=${user.id}" class="btn bg-olive btn-xs">詳情</a>      

2、編寫Controller

/**
     * 查詢某使用者的詳情功能
     *  使用者有哪些角色 角色分别有哪些權限
     * @param userId
     * @return
     */
    @RequestMapping("/details")
    public ModelAndView details(Integer userId){
        LogUtils.print("使用者詳情:"+userId);
        //查詢資料--使用者詳情
        SysUser user=userService.findById(userId);
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("user",user);
        modelAndView.setViewName("user-show");
        return modelAndView;
    }      

3、編寫Service

findById(Integer userId);      
@Override
    public SysUser findById(Integer userId) {
        return userdao.findById(userId);
    }      

4、編寫Dao

編寫userDao
@Select("select * from sys_user where id = #{userId}")
    @Results({
            @Result(id=true,property = "id",column = "id"),//主鍵id 最好都加上 否則後端擷取id為null
            @Result(property = "roles",column = "id", //還是本張表主鍵id,因為是根據本張表主鍵id關聯到role的
                    //根據userId查詢角色清單
                    // cn.ahpu.dao.RoleDao.findRolesByUserId
                    many = @Many(select = "cn.ahpu.dao.RoleDao.findRolesByUserId"))
    })
    SysUser findById(Integer userId);      
編寫RoleDao(user下有role)
@Select("select r.* from sys_user_role ur,sys_role r where ur.roleid=r.id and ur.userid=#{userId}")
    @Results({
            @Result(id=true,property = "id",column = "id"),//主鍵id 最好加上 否則id隻能用一次 後端查id就是null了
            @Result(property = "permissions",column = "id", //還是本張表主鍵id,因為是根據本張表主鍵id關聯到role的
                    many = @Many(select = "cn.ahpu.dao.PermissionDao.findPermissionsByRoleId"))
    })
    List<Role> findRolesByUserId(Integer userId);      
編寫PermissionDao(Role下有permission)
@Select("select p.* from sys_role_permission rp,sys_permission p where rp.roleid=#{roleId} and rp.permissionid=p.id")
    List<Permission> findPermissionsByRoleId(Integer roleId);      

5、修改前端user-show.jsp

<thead>
  <tr>
    <th>名稱</th>
    <th>描述</th>
  </tr>
</thead>

<tr data-tt-id="0">
  <td colspan="2">${user.username}</td>
</tr>

<tbody>
  <c:forEach items="${user.roles}" var="role" varStatus="i">
    <tr data-tt-id="${role.id}" data-tt-parent-id="0">
      <td>${role.roleName }</td>
      <td>${role.roleDesc }</td>
    </tr>
    <%--子标簽的父id要唯一對應 得用${i.count}了 }
      若dao中id@Result()裡id都單獨配置了
      那麼id可以多次使用 将i.count全部換成 role.id也不錯
    --%>
    <c:forEach items="${role.permissions}" var="permission">
      <tr data-tt-id="${role.id}-${permission.id}" data-tt-parent-id="${role.id}">
        <td>${permission.permissionName}</td>
        <td>${permission.url}</td>
      </tr>
    </c:forEach>
  </c:forEach>
</tbody>      

第三節:為使用者配置設定角色-角色清單資料回顯

1、頁面入口

SSM練習第五天

2、編寫Controller

@RequestMapping("/addRoleToUserUI")
    public ModelAndView addRoleToUserUI(Integer userId){
        LogUtils.print("addRoleToUserUI:"+userId);
        //所有角色
        List<Role> roles = roleService.findAll();
        //目前使用者擁有的角色 直接将目前使用者傳過去也行 延遲加載還能起點作用
        SysUser user = userService.findById(userId);
        //友善選中已有的 換種寫法
        List<Role> userOwnedRoles = user.getRoles();
        //把該使用者擁有的角色id存到list放到前端
        //id變成一個list 前端友善判斷該id是否在數組中 el ${fn:contains(ids,id)}
        List<Integer> ids=new ArrayList<>();
        for (Role role : userOwnedRoles) {
            ids.add(role.getId());
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("roles",roles);
        modelAndView.addObject("ids",ids);
        //真正執行添加業務時需要userId 是以也需要傳過去
        modelAndView.addObject("userId",user.getId());//user.getId()而不直接寫userId "永遠使用最後一次得到的資料"
        modelAndView.setViewName("user-role-add");
        return modelAndView;
    }      

3、頁面回顯資料(user-role-add.jsp)

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<section class="content">
  <input type="hidden" name="userId" value="${userId}"><%--使用者id隐藏域--%>
  <table id="dataList"
      class="table table-bordered table-striped table-hover dataTable">
      <thead>
        <tr>
          <th class=""style="padding-right:">
          <input id="selall" 
            type="checkbox" class="icheckbox_square-blue"></th>
          <th class="sorting_asc">ID</th>
          <th class="sorting">角色名稱</th>
          <th class="sorting">角色描述</th>                  
        </tr>
      </thead>
      <tbody>

        <c:forEach items="${roles}" var="role">
          <tr>
            <td><input name="ids" type="checkbox" value="${role.id}"
              <%--<c:if test="${fn:contains(ids, role.id)}">checked="checked"</c:if>--%>
                <%--大神更牛的寫法--%>
                ${fn:contains(ids, role.id)?"checked":" "}

            ></td>
            <td>${role.id}</td>
            <td>${role.roleName }</td>
            <td>${role.roleDesc}</td>
            
          </tr>
        </c:forEach>
      </tbody>

    </table>      

第四節:為使用者配置設定角色-更新關系到使用者角色中間表

1、頁面入口

SSM練習第五天

2、編寫Controller

/**
     * 給指定使用者添加指定角色
     * @param userId 需要添加角色的使用者id
     * @param ids    添加角色的id數組
     * @return
     */
    @RequestMapping("/addRolesToUser")
    public String addRolesToUser(Integer userId,Integer[] ids){
        userService.addRolesToUser(userId,ids);
        return "redirect:/user/findAll";
    }      

3、編寫Service

接口

/**
     * 添加角色清單到使用者
     * @param userId
     * @param ids
     */
    void addRolesToUser(Integer userId, Integer[] ids);      

實作

@Override
    public void addRolesToUser(Integer userId, Integer[] ids) {
        //先清空該使用者擁有的所有角色
        userdao.delRolesFromUser(userId);

        if(ids!=null){
            for (Integer roleId : ids) {
                userdao.saveRoleToUser(userId,roleId);
            }
        }
    }      

4、編寫Dao

/**
     * 清空使用者原來有的所有角色
     * @param userId
     */
    @Delete("delete from sys_user_role where userId=#{userId}")
    void delRolesFromUser(Integer userId);

    /**
     * 為使用者添加一個角色
     * @param userId
     * @param roleId
     */
    @Insert("insert into sys_user_role values(#{param1},#{param2})")
    void saveRoleToUser(Integer userId, Integer roleId);      

第五節:為角色添權重限資料回顯(和user添加role雷同)

1、頁面入口

SSM練習第五天
<a href="${pageContext.request.contextPath}/role/addPermissionsToRoleUI?roleId=#{role.id}" class="btn bg-olive btn-xs">添權重限</a>      

2、編寫Controller

/**
     * 添權重限到角色的資料回顯
     * @param roleId
     * @return
     */
    @RequestMapping("/addPermissionsToRoleUI")
    public ModelAndView addPermissionsToRoleUI(Integer roleId){
        LogUtils.print(roleId);

        //擷取所有權限
        List<Permission> permissions = permissionService.findAll();

        //擷取角色已有權限id清單
        Role role = roleService.findById(roleId);
        List<Permission> rolePermissions = role.getPermissions();
        List<Integer> ids=new ArrayList<>();
        for (Permission p : rolePermissions) {
            ids.add(p.getId());
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("permissions",permissions);
        modelAndView.addObject("ids",ids);
        modelAndView.addObject("roleId",roleId);
        modelAndView.setViewName("role-permission-add");

        return modelAndView;
    }      

3、編寫Service

findById(Integer roleId);      
@Override
    public Role findById(Integer roleId) {
        return roleDao.findById(roleId);
    }      

4、編寫Dao

@Select("select * from sys_role where id=#{roleId}")
    @Results({
            @Result(property = "id",column = "id"),
            @Result(property = "permissions",column = "id",
                    many=@Many(select = "cn.ahpu.dao.PermissionDao.findPermissionsByRoleId",fetchType = FetchType.LAZY)
            )
    })
    Role findById(Integer roleId);      

5、前端頁面(role-permission-add.jsp)

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<form
  action="${pageContext.request.contextPath}/role/addPermissionToRole.do"
  method="post">
  <!-- 正文區域 -->
  <section class="content"> <input type="hidden" name="roleId"
    value="${roleId}">
    <table id="dataList"
        class="table table-bordered table-striped table-hover dataTable">
        <thead>
          <tr>
            <th class=""style="padding-right:">
            <input id="selall" 
              type="checkbox" class="icheckbox_square-blue"></th>
            <th class="sorting_asc">ID</th>
            <th class="sorting">權限名稱</th>
            <th class="sorting">URL</th>                  
          </tr>
        </thead>
        <tbody>
          <c:forEach items="${permissions}" var="permission">
            <tr>
              <td><input name="ids" type="checkbox" value="${permission.id}"
                     ${fn:contains(ids, permission.id)?"checked":""}
              ></td>
              <td>${permission.id}</td>
              <td>${permission.permissionName }</td>
              <td>${permission.url}</td>
              
            </tr>
          </c:forEach>
        </tbody>

      </table>
  <!--訂單資訊/--> <!--工具欄-->
  <div class="box-tools text-center">
    <button type="submit" class="btn bg-maroon">儲存</button>
    <button type="button" class="btn bg-default"
      onclick="history.back(-1);">傳回</button>
  </div>
  <!--工具欄/--> </section>
  <!-- 正文區域 /-->
</form>      

第六節:為角色添權重限-實際儲存即往中間表插入記錄(和user添加role雷同)

1、頁面入口

SSM練習第五天
SSM練習第五天

2、編寫Controller

/**
     * 為角色添權重限 動資料庫表
     * @param roleId
     * @param ids
     * @return
     */
    @RequestMapping("/addPermissionsToRole")
    public String addPermissionsToRole(Integer roleId,Integer[] ids){
        LogUtils.print(ids);
        LogUtils.print(roleId);

        roleService.addPermissionsToRole(roleId,ids);
        return "redirect:/role/findAll";
    }      

3、編寫service

void addPermissionsToRole(Integer roleId, Integer[] ids);      
/**
     * 為角色roleId 添加 ids這麼多的權限
     * @param roleId
     * @param ids
     */
    @Override
    public void addPermissionsToRole(Integer roleId, Integer[] ids) {
        //删除roleId所有權限
        roleDao.delPermissionsFromRole(roleId);

        if(ids!=null){
            for (Integer permissionId : ids) {
                roleDao.savePermissionToRole(roleId,permissionId);
            }
        }

    }      

4、編寫dao

@Delete("delete from sys_role_permission where roleid=#{roleId}")
    void delPermissionsFromRole(Integer roleId);

    /**
     * 注意參數順序
     * @param roleId
     * @param permissionId
     */
    @Insert("insert into sys_role_permission values(#{param2},#{param1})")
    void savePermissionToRole(Integer roleId, Integer permissionId);      

第七節:為使用者設定真正的角色

修改UserServiceImpl的loadUserByUsername方法,為使用者設定真正的角色

/**
     * 通過使用者名 得到使用者對象
     * 建立使用者詳情對象,傳回
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根據使用者名擷取使用者(SysUser)對象--同時查詢相應角色
        SysUser sysUser = userdao.findByUsername(username);
        if(sysUser==null) return null;
        /* 添加的都是假的角色對象  真正的角色對象需要到資料庫内查
        //配置檔案裡沒有指定角色了 需要自己建立角色對象
        //建立角色的集合對象
        Collection<GrantedAuthority> authorities=new ArrayList<>();
        //建立臨時角色對象  正常情況下應該是資料庫角色表中查的
        GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_USER");
        //對象添加到集合中
        authorities.add(grantedAuthority);
        //User是安全架構内實作了UserDetails接口的一個類
        //第三個參數是:角色清單對象 (此處角色名ROLE_USER自己定義 xml裡需要這個名字)
        //{noop}字首表示不加密  該把不加密去掉了*/

        //添加資料庫裡真正的角色對象
        Collection<GrantedAuthority> authorities=new ArrayList<>();
        //登入時授權給使用者 有哪些權限就授予哪些權限
        for (Role role : sysUser.getRoles()) {
            //LogUtils.print(role.getRoleName());
            //建立角色對象
            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_"+role.getRoleName());
            //添加角色對象到集合
            authorities.add(grantedAuthority);
        }

        //使用者名 密碼 角色
        UserDetails user = new User(sysUser.getUsername(),sysUser.getPassword(),authorities);
        return user;
    }      

userdao.findByUsername(username);,加個一對多結果集映射,否則查不到使用者所擁有的權限

//根據使用者名查詢使用者對象 唯一對象(username必須唯一)
    @Select("select * from sys_user where username = #{username} and status=1")
    @Results({
            @Result(id=true,property = "id",column = "id"),//主鍵id 最好都加上 否則後端擷取id為null
            @Result(property = "roles",column = "id", //還是本張表主鍵id,因為是根據本張表主鍵id關聯到role的
                    many = @Many(select = "cn.ahpu.dao.RoleDao.findRolesByUserId",fetchType = FetchType.LAZY))
    })
    SysUser findByUsername(String username);      

第二章:授權後的安全控制

第一節:在JSP頁面控制菜單權限(不顯示UI)

在JSP頁面中使用security:authorize标簽,可以控制菜單是否顯示。security:authorize标簽的 access=“hasAnyRole(‘ROLE_USER’,‘ROLE_ADMIN’)”。因為标簽的access使用的是表達式,是以需要将spring- security.xml配置檔案的use-expressions設定為true。

1、spring-security.xml配置檔案

1、使用表達式改為true: use-expressions=“true”

2、access不能直接寫權限名而要寫成:access="hasAnyRole(‘ROLE_USER’,‘ROLE_ADMIN’)”

表示擁有’ROLE_USER’,'ROLE_ADMIN’中任意一個權限就初步可以通路/**任意路徑(真正目的是啥權限都沒有時無法通路本網站任意未放行路徑)

3、前端jsp 先加上标簽庫 然後

系統管理UI用 <security:authorize access=“hasRole(‘ROLE_ADMIN’)”></security:authorize>包裹

基礎資料UI用<security:authorize access=“hasAnyRole(‘ROLE_ADMIN’,‘ROLE_USER’)”></security:authorize>包裹

SSM練習第五天

jsp:

<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<security:authorize access="hasRole('ROLE_ADMIN')">
  <li class="treeview"><a href="#"> <i class="fa fa-cogs"></i>
      <span>系統管理</span> <span class="pull-right-container"> <i
        class="fa fa-angle-left pull-right"></i>
    </span>
  </a>
    <ul class="treeview-menu">
  
      <li id="system-setting"><a
        href="${pageContext.request.contextPath}/user/findAll"> <i
          class="fa fa-circle-o"></i> 使用者管理
      </a></li>
      <li id="system-setting"><a
        href="${pageContext.request.contextPath}/role/findAll"> <i
          class="fa fa-circle-o"></i> 角色管理
      </a></li>
      <li id="system-setting"><a
        href="${pageContext.request.contextPath}/permission/findAll">
          <i class="fa fa-circle-o"></i> 權限管理
      </a></li>
      <li id="system-setting"><a
        href="${pageContext.request.contextPath}/pages/syslog-list.jsp"> <i
          class="fa fa-circle-o"></i> 通路日志
      </a></li>
    </ul></li>
</security:authorize>

<%--ROLE_ADMIN和ROLE_USER都可以使用 換言之啥權限都沒有連基礎資料都不能通路--%>
<security:authorize access="hasAnyRole('ROLE_ADMIN','ROLE_USER')">
  <li class="treeview"><a href="#"> <i class="fa fa-cube"></i>
      <span>基礎資料</span> <span class="pull-right-container"> <i
        class="fa fa-angle-left pull-right"></i>
    </span>
  </a>
    <ul class="treeview-menu">
  
      <li id="system-setting"><a
        href="${pageContext.request.contextPath}/product/findAll">
          <i class="fa fa-circle-o"></i> 産品管理
      </a></li>
      <li id="system-setting"><a
        href="${pageContext.request.contextPath}/order/findAll">
          <i class="fa fa-circle-o"></i> 訂單管理
      </a></li>
    </ul></li>
</security:authorize>      

第二節:在伺服器端控制權限(攔截請求 防止使用者直接輸入url通路)

雖然頁面菜單對不同角色顯示類不同的菜單,但是如果直接通路伺服器端的url還是可以通路到資源資訊的,是以好 需要對伺服器端的資源進行安全控制。

控制方式就是借助于Spring的AOP,對Controller的的通路進行權限的功能增強。 修改spring-security.xml配置檔案,添加aop的自動代理

1、修改spring-mvc.xml配置

添加配置:

<!--AOP 當下面一行的配置也放在spring-mvc.xml本檔案内時此配置可有可無
        當下面一行配置放在spring-security.xml中 本配置必須有 開啟注解動态代理
-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
<!--配置開啟security的注解支援 必須加上-->
<security:global-method-security secured-annotations="enabled"/>      

沒辦法提示了,自己手動加: 複制後改下名字即可

SSM練習第五天

2、在需要進行控制的Controller上添加@Secured注解

指定擁有哪些權限時方可通路本Controller,使用者任意一個權限即可

@Secured({"ROLE_ADMIN"})
public class UserController
@Secured({"ROLE_ADMIN"})
public class RoleController
@Secured({"ROLE_ADMIN"})
public class PermissionController

@Secured({"ROLE_ADMIN","ROLE_USER"})
public class ProductController      

3、更換預設403 forbidden界面

spring-security.xml裡加一行配置

<security:access-denied-handler error-page="/failer.jsp"></security:access-denied-handler>      
SSM練習第五天

第三章:系統日志功能

第一節:AOP記錄日志

1、日志表sys_log和實體Log

sql語句

create sequence log_seq;

CREATE TABLE sys_log(
    id number PRIMARY KEY,
    visitTime DATE,
    username VARCHAR2(50),
    ip VARCHAR2(30),
    method VARCHAR2(200)
)      

實體類

SysLog.java

@Data
public class SysLog {
    private Long id;
    private Date visitTime;//通路時間
    private String username;//通路者使用者名
    private String ip;//通路者ip
    private String method;//通路的全限定類名.方法
}      

2、springmvc.xml配置檔案中開啟aop的自動代理

之前應該已經加過了

<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>      

3、在web.xml中配置監聽request對象的監聽器

springmvc會自動将httpservletrequest放到IOC容器中 即springmvc不需要加此配置

其他架構不會 其他web層架構想要注入request就必須加此配置

<!--配置請求監聽器:當請求發生時,在容器中建立請求對象-->
  <listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  </listener>      

4、編寫切面類(切面類内部有增強)

SSM練習第五天

spring-mvc.xml得多掃描一個包了

SSM練習第五天

LogAop代碼實作

package cn.ahpu.log;

import cn.ahpu.domain.SysLog;
import cn.ahpu.domain.SysUser;
import cn.ahpu.service.LogService;
import cn.ahpu.utils.LogUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

/**
 * @author 寒面銀槍
 * @create 2020-05-16 16:53
 *
 * 注解配置AOP
 * 提供一個類配置為切面類
 *  切面=切入點+通知
 *      通知類型
 *          前置增強:@Before
 *          後置增強:@AfterReturning
 *          最終增強:@After
 *          異常增強:@AfterThrowing
 *          環繞增強:@Around
 *
 */
//注意xml裡加上掃描log包
@Component
@Aspect
public class LogController {

    @Autowired
    LogService logService;

    @Autowired
    HttpServletRequest request;//隻有springmvc放到IOC中了 換一個架構可能就沒了

    @Pointcut("execution(* cn.ahpu.controller.*.*(..))")
    public void pointcut(){}

    /**
     * 環繞增強
     * @param joinPoint 連接配接點對象--可以執行真實方法--隻在環繞增強中使用
     *        連接配接點就是攔截的方法
     */
    @Around("pointcut()") //織入
    public Object around(ProceedingJoinPoint joinPoint){
        //建立日志對象
        SysLog sysLog = new SysLog();

        //将日志對象封裝
        //1.通路時間 visitTime
        sysLog.setVisitTime(new Date());

        //2.通路使用者名 username 安全架構内得到 注意是安全架構的User 不是自己寫SysUser
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        sysLog.setUsername(user.getUsername());

        //3.通路ip 還是得得到request對象 仔細想想哪裡有? IOC容器裡肯定有 直接注入一下就行了嘛
        String ipAddr = request.getRemoteAddr();
        sysLog.setIp(ipAddr);

        //4.通路全限定類名
        //被攔截類的全限定類名
        String className = joinPoint.getTarget().getClass().getName();
        //方法名稱
        String methodName = joinPoint.getSignature().getName();
        sysLog.setMethod(className+"."+methodName);

        //将日志對象存儲到資料庫
        LogUtils.print(sysLog);
        logService.save(sysLog);


        try {
            //執行真實的方法--必須傳回真實方法傳回值 否則所有方法被攔截 傳回值都不傳回 就不會跳轉了
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

}      

5、Service代碼實作

LogService

public interface LogService {
    void save(SysLog sysLog);
}      
@Service
public class LogServiceImpl implements LogService {

    @Autowired
    LogDao logDao;

    @Override
    public void save(SysLog log) {
        logDao.save(log);
    }
}      

6、Dao代碼實作

public interface LogDao {
    @Insert("insert into sys_log values(log_seq.nextval,#{visitTime},#{username},#{ip},#{method})")
    void save(SysLog log);
}      

第二節 查詢日志

1、頁面入口

SSM練習第五天
SSM練習第五天

2、編寫Controller

/**
     * 查詢所有日志  分頁查詢
     */
    @RequestMapping("/findAll")
    public ModelAndView findAll(
            @RequestParam(value = "currPage",required = false,defaultValue = "1") Integer currPage,
            @RequestParam(value = "pageSize",required = false,defaultValue = "5") Integer pageSize
    ){
        PageInfo<SysLog> pageHelper = logService.findAllByPageHelper(currPage, pageSize);

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("pageHelper", pageHelper);
        modelAndView.setViewName("syslog-list");

        return modelAndView;
    }      

3、編寫service

public interface LogService {

    void save(SysLog sysLog);

    /**
     * 查詢所有日志
     * @return
     */
    List<SysLog> findAll();

    /**
     * 分頁查詢所有
     * @param currPage
     * @param pageSize
     * @return
     */
    PageInfo<SysLog> findAllByPageHelper(Integer currPage, Integer pageSize);
}      
@Override
    public PageInfo<SysLog> findAllByPageHelper(Integer currPage, Integer pageSize) {
        //指定分頁參數
        PageHelper.startPage(currPage,pageSize);
        //查詢全部 分頁參數已經被綁定到目前線程 攔截器根據參數幫你做到攔截
        List<SysLog> logs = logDao.findAll();
        //建立PageInfo對象 控制頁面最多顯示5個頁碼
        PageInfo<SysLog> pageInfo = new PageInfo<>(logs, 5);
        //傳回
        return pageInfo;
    }      

4、編寫dao

@Select("select * from sys_log")
    List<SysLog> findAll();      

5、修改domain

@Data
public class SysLog {
    private Long id;
    private Date visitTime;//通路時間
    private String visitTimeStr;
    private String username;//通路者使用者名
    private String ip;//通路者ip
    private String method;//通路的全限定類名.方法

    public String getVisitTimeStr() {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm");
        String timeStr = sdf.format(visitTime);
        return timeStr;
    }
}      

6、修改頁面

<table id="dataList"
      class="table table-bordered table-striped table-hover dataTable">
      <thead>
        <tr>
          <th class=""style="padding-right:"><input id="selall"
            type="checkbox" class="icheckbox_square-blue"></th>
          <th class="sorting_asc">ID</th>
          <th class="sorting">通路時間</th>
          <th class="sorting">通路使用者</th>
          <th class="sorting">通路IP</th>
          <th class="sorting">通路方法</th>
        </tr>
      </thead>
      <tbody>
        <c:forEach items="${pageHelper.list}" var="syslog" varStatus="i">
          <tr>
            <td><input name="ids" type="checkbox"></td>
            <td>${i.count}</td>
            <td>${syslog.visitTimeStr }</td>
            <td>${syslog.username }</td>
            <td>${syslog.ip }</td>
            <td>${syslog.method}</td>
          </tr>
        </c:forEach>
      </tbody>

    </table>
    <!--資料清單/-->

    <!--工具欄-->
    <div class="pull-left">
      <div class="form-group form-inline">
        <div class="btn-group">
          <button type="button" class="btn btn-default" title="重新整理"
            onclick="window.location.reload();">
            <i class="fa fa-refresh"></i> 重新整理
          </button>
        </div>
      </div>
    </div>
    <div class="box-tools pull-right">
      <div class="has-feedback">
        <input type="text" class="form-control input-sm"
          placeholder="搜尋"> <span
          class="glyphicon glyphicon-search form-control-feedback"></span>
      </div>
    </div>
    <!--工具欄/-->
  </div>
  <!-- 資料表格 /-->

</div>
<!-- /.box-body -->

<!-- .box-footer-->
<div class="box-footer">
  <div class="pull-left">
    <div class="form-group form-inline">
      第${pageHelper.pageNum} 頁,
      總共${pageHelper.pages} 頁,共${pageHelper.total} 條資料。 每頁
      <select class="form-control" name="pageSize" id="pageSize" onchange="gotoPage(1)">
        <%--前10個用循環寫 友善--%>
        <c:forEach begin="1" end="10" var="i">
          <option value="${i}">${i}</option>
        </c:forEach>
        <option value="15">15</option>
        <option value="20">20</option>
        <option value="30">30</option>
        <option value="40">40</option>
        <option value="50">50</option>

      </select> 條
    </div>
  </div>

  <div class="box-tools pull-right">
    <ul class="pagination" id="gotoLi">
      <%--超連結裡通路js函數 必須加字首javascript:--%>
      <li><a href="javascript:gotoPage(1)" aria-label="Previous">首頁</a></li>
      <%--prePage多友善--%>
      <li><a href="javascript:gotoPage(${pageHelper.prePage})">上一頁</a></li>
      <%--頁面上顯示幾個頁碼也簡單可控了 實在太友善了--%>
      <c:forEach begin="${pageHelper.navigateFirstPage}" end="${pageHelper.navigateLastPage}" var="i">
        <li><a href="javascript:gotoPage(${i})">${i}</a></li>
      </c:forEach>
      <li><a href="javascript:gotoPage(${pageHelper.nextPage})">下一頁</a></li>
      <li><a href="javascript:gotoPage(${pageHelper.pages})" aria-label="Next">尾頁</a></li>
    </ul>
  </div>

</div>      
<script type="text/javascript">
        //方法寫在外面 會自動執行 注意先得給每個option加上value="xx" 不能沒有value屬性
        //每頁顯示幾條的資料回顯 select option
        $("#pageSize option[value=${pageHelper.pageSize}]").prop("selected","selected");

        //跳轉頁面
        function gotoPage(currPage) {
            //頁面的越界檢查此處判斷也很友善
            if(currPage<1||currPage>${pageHelper.pages}) return;

            var pageSize=$("#pageSize").val();
            location.href="${pageContext.request.contextPath}/log/findAll?currPage="+currPage+"&pageSize="+pageSize;
        }

  </script>