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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章