JavaEE學習日誌(一百一十): SpringSecurity實現權限管理,AOP管理日誌

SpringSecurity

給用戶添加真正的角色

在UserDao中的findByUsername方法,增加查詢roleList

/**
     * 根據用戶名查詢用戶對象(唯一)
     * @param username
     * @return
     */
    @Select("select * from sys_user where username = #{username} and status = 1")
    @Results({
            @Result(property = "id", column = "id"),
            @Result(property = "roleList", column = "id", javaType = List.class,
                    //根據userId查詢角色列表findRoleListByUserId
                    many = @Many(select = "com.itheima.dao.RoleDao.findRoleListByUserId", fetchType = FetchType.LAZY))
    })
    SysUser findByUsername(String username);

給用戶添加上真正的角色

/**
     * 通過用戶名得到用戶對象
     * 創建用戶詳情對象,返回
     * @param username
     * @return UserDetails:用戶詳情
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根據用戶名獲取用戶(SysUser)對象 -- 同時查詢相應的角色
        SysUser sysUser = userDao.findByUsername(username);
        System.out.println(username);
        System.out.println(sysUser);
        if(sysUser!=null){
//            //創建角色集合對象    都是假的角色對象
//            Collection<GrantedAuthority> authorities = new ArrayList<>();
//            //創建臨時角色對象
//            SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_USER");
//            //對象添加到集合中
//            authorities.add(grantedAuthority);
            Collection<GrantedAuthority> authorities = new ArrayList<>();
            for (Role role : sysUser.getRoleList()) {
                SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_"+role.getRoleName());
                authorities.add(grantedAuthority);
            }

            /**
             * 參數1:username
             * 參數2:password
             * 參數3:角色列表對象
             */
            UserDetails user = new User(sysUser.getUsername(),sysUser.getPassword(),authorities);
            return user;
        }

        return null;

    }

修改spring-security.xml

<security:http auto-config="true" use-expressions="true">
        <!-- 配置攔截的請求地址,任何請求地址都必須有ROLE_ADMIN,ROLE_USER的權限 -->
        <security:intercept-url pattern="/**" access="hasAnyRole('ROLE_ADMIN','ROLE_USER')"/>
        <!--指定安全框架使用的頁面-->
        <!--
            login-page:指定登錄頁面
            login-processing-url:登錄的請求路徑,登錄時必須使用該路徑
            default-target-url:登錄成功後進入的頁面
            authentication-failure-url:認證失敗要進入的頁面
        -->
        <security:form-login login-page="/login.jsp"
                             login-processing-url="/login"
                             default-target-url="/index.jsp"
                             authentication-failure-url="/login.jsp"

        ></security:form-login>
        <!--關閉跨站請求僞造-->
        <security:csrf disabled="true"></security:csrf>
        <!-- 退出 -->
        <security:logout invalidate-session="true" logout-url="/logout" logout-success-url="/login.jsp"/>
        <!--如果訪問被拒絕,則跳轉失敗頁面-->
        <security:access-denied-handler error-page="/failer.jsp"></security:access-denied-handler>
    </security:http>

控制前端頁面

  1. 引入security標籤庫
  2. <security:authorize access="hasRole('ROLE_ADMIN')">將需要管理員身份的部分括起來
<%@ page language="java" isELIgnored="false" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<aside class="main-sidebar">
	<!-- sidebar: style can be found in sidebar.less -->
	<section class="sidebar">
		<!-- Sidebar user panel -->
		<div class="user-panel">
			<div class="pull-left image">
				<img src="${pageContext.request.contextPath}/img/user2-160x160.jpg"
					class="img-circle" alt="User Image">
			</div>
			<div class="pull-left info">
				<p>
					<security:authentication property="principal.username" />
				</p>
				<a href="#"><i class="fa fa-circle text-success"></i> 在線</a>
			</div>
		</div>

		<!-- sidebar menu: : style can be found in sidebar.less -->
		<ul class="sidebar-menu">
			<li class="header">菜單</li>
			<li id="admin-index"><a
				href="${pageContext.request.contextPath}/pages/main.jsp"><i
					class="fa fa-dashboard"></i> <span>首頁</span></a></li>
			<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>
			<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>

		</ul>
	</section>
	<!-- /.sidebar -->
</aside>

此時只有用戶權限的用戶將只會看到基礎數據部分
在這裏插入圖片描述
但服務器端沒有被控制,當訪問路徑正確時,還是能訪問到被管理員控制的部分

控制服務端

一、在spring-mvc.xml中添加註解

  1. 開啓aop的自動代理(這句話加不加好像都能用,但加上這句話,下面這句就能寫在spring-security.xml中,否則不行
  2. 配置security的註解支持(主要加這句話)
 <!--開啓aop的自動代理-->
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
    <!--配置security的註解支持-->
    <security:global-method-security secured-annotations="enabled"></security:global-method-security>

二、給只能被管理員訪問的模塊上加上
@Secured({"ROLE_ADMIN"})

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

aop管理日誌

創建日誌表和實體類

create sequence log_seq;

CREATE TABLE sys_log(
    id number PRIMARY KEY,
    visitTime DATE,
    username VARCHAR2(50),
    ip VARCHAR2(30),
    method VARCHAR2(200)
)
public class SysLog {
    private Long id;
    //訪問時間
    private Date visitTime;
    //訪問的用戶名
    private String username;
    //訪問的ip地址
    private String ip;
    //訪問的全限類名.方法
    private String method;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Date getVisitTime() {
        return visitTime;
    }

    public void setVisitTime(Date visitTime) {
        this.visitTime = visitTime;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }
}

LogController

掃描log包,創建對象

<context:component-scan base-package="com.itheima.log"></context:component-scan>

配置切面

注意:如果不用springmvc如何得到request對象?
配置請求監聽器,當請求發生時,在容器中創建請求對象

<!--配置請求監聽器,當請求發生時,在容器中創建請求對象-->
  <listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  </listener>
/**
 * 註解配置AOP
 * 提供一個類,配置爲切面類
 * 切面:切入點+通知
 * 通知:
 *  1.前置增強:@Before
 *  2.後置增強: @AfterReturning
 *  3.最終增強:@After
 *  4.異常增強:@AfterThrowing
 *  5.環繞增強:@Around
 *
 *
 */
@Component
@Aspect
public class LogController {
    @Autowired
    LogService logService;
    @Autowired
    HttpServletRequest request;
    /**
     * 切入點
     */
    @Pointcut("execution(* com.itheima.controller.*.*(..))")
    public void pointcut(){}

    /**
     * 織入
     * 環繞增強
     * ProceedingJoinPoint:連接點對象,可以執行真實對象的真實方法,只在環繞增強中使用
     * JoinPoint:連接點對象,不可以執行真實對象的真實方法
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint){
        //創建日誌對象
        SysLog log = new SysLog();
        //將日誌對象封裝
        //1. 訪問時間
        log.setVisitTime(new Date());
        //2. 訪問用戶名,從安全框架得到
        //獲取securityContext對象
        SecurityContext context = SecurityContextHolder.getContext();
        //獲取認證對象
        Authentication authentication = context.getAuthentication();
        //獲取用戶對象
        User user = (User)authentication.getPrincipal();
        //獲取用戶名
        String username = user.getUsername();
        log.setUsername(username);
        //3. 訪問的ip地址,從請求對象獲得
        //getRemoteAddr()獲取ip地址
        String ip = request.getRemoteAddr();
        log.setIp(ip);
        //4. 訪問的全限類名.方法
        //得到被攔截的對象
        Object target = joinPoint.getTarget();
        //獲取被攔截的類的全限類名
        String className = target.getClass().getName();
        //獲取被攔截的方法
        Signature signature = joinPoint.getSignature();
        //獲取方法名
        String methodName = signature.getName();
        //將日誌對象存儲到數據庫中
        log.setMethod(className+"."+methodName);
        //System.out.println(log);
        logService.save(log);
        try {
            //執行真實的方法,需要把真實方法的返回值返回,如果不返回,所有方法被攔截,沒有執行返回值
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

}

LogService

@Service
public class LogServiceImpl implements LogService {
    @Autowired
    LogDao logDao;
    @Override
    public void save(SysLog log) {
        logDao.save(log);
    }
}

LogDao

public interface LogDao {
    /**
     * 保存日誌
     * @param log
     */
    @Insert("insert into sys_log values(log_seq.nextval,#{visitTime},#{username},#{ip},#{method})")
    void save(SysLog log);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章