(八)Spring安全框架的开发(没写完)

  几乎只要是系统永远都存在有一个功能--登录与权限分配;而Spring考虑到了这种情况,专门提供了一个用于登录验证以及单Session验证,角色分配的框架概念--Spring安全框架

开发准备:

    本次将采用Spring MVC的结构进行安全框架的开发.

新建一个项目:SpringSecurityProject


  

    Spring安全框架的设计理念在于:你可以直接在开发完成的项目上配置安全框架.

范例:在web.xml里面配置Spring的相关信息

  <servlet>
    <servlet-name>springmvc</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<init-param>
  	<param-name>contextConfigLocation</param-name>
  	<param-value>classpath:applicationContext.xml</param-value>
  	</init-param>
  </servlet>
  <servlet-mapping>
  <servlet-name>springmvc</servlet-name>
  <url-pattern>*.action</url-pattern>
  </servlet-mapping>
  <filter>
  	<filter-name>encodingFilter</filter-name>
  	<filter-class>org.springframework.web.filter.CharacterEncodingFilter
  	</filter-class>
  	<init-param>
  		<param-name>encoding</param-name>
  		<param-value>UTF-8</param-value>
  	</init-param>
  </filter>
  <filter-mapping>
  <filter-name>encodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
  </filter-mapping>

  2.修改applicationContext.xml文件定义springMVC相关配置信息;

	<mvc:annotation-driven/>
	<mvc:default-servlet-handler/>
			<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
			<property name="prefix" value="/WEB-INF/"></property>
			<property name="suffix" value=".jsp"></property>
		</bean>
		<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
				<property name="basenames">
					<array>
						<value>Message</value>
						<value>Pages</value>
					</array>
				</property>
		</bean>

        此时所有的相关页面都要求在WEB-INF目录下保存.

3.定义Message.java文件.

package cn.zwb.vo;

import java.io.Serializable;

@SuppressWarnings("serial")
public class Message implements Serializable{
	private Integer mid;
	private String title;
	private String content;
	public Integer getMid() {
		return mid;
	}
	public void setMid(Integer mid) {
		this.mid = mid;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	
}

4.定义MessageAction.java程序类

package cn.zwb.action;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import cn.zwb.vo.Message;

@Controller
@RequestMapping("/pages/back/message/*")
public class MessageAction {
	@RequestMapping("message_insertpre")
	public ModelAndView insertPre(){
		ModelAndView mav=new ModelAndView("back/message/message_insert");
		return mav;
	}
	@RequestMapping("message_insert")
	public ModelAndView insert(Message msg){
		System.out.println("[添加数据]"+msg);
		ModelAndView mav =new ModelAndView("forward");
		mav.addObject("msg","信息添加成功!");
		mav.addObject("url","/pages/back/message/message_insertpre.action");
		return mav;
	}
	@RequestMapping("message_list")
	public ModelAndView list(){
		ModelAndView mav=new ModelAndView("/back/message/message_list");
		List<Message> all=new ArrayList<Message>();
		for (int i = 0; i < 10; i++) {
			Message msg=new Message();
			msg.setMid(i);
			msg.setTitle("mldn-"+i);
			msg.setContent("www.baidu.com");
			all.add(msg);
		}
		return mav;
	}
	@RequestMapping("message_delete")
	public ModelAndView delete (Integer mid){
		System.out.println("[删除数据]数据编号为:"+mid);
		ModelAndView mav =new ModelAndView("forward");
		mav.addObject("msg","信息添加成功!");
		mav.addObject("url","/pages/back/message/message_list.action");
		return mav;
	}
}

5.准备出相关的几个界面

            增加数据页面

<body>
	<form action="pages/back/message/message_insert.action">
		消息编号:<input type="text" name="mid" id="mid"><br>
		新闻标题:<input type="text" name="title" id="title"><br>
		新闻内容:<input type="text" name="content" id="content">
		<input type="submit" value="发布">
		<input type="reset" value="重置">
	</form>
</body>

           显示与删除

<%@page import="java.util.Iterator"%>
<%@page import="java.util.List"%>
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
	<base href="<%=basePath%>">

	<title>My JSF 'MyJsp.jsp' starting page</title>
	
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->

</head>
  
<body>
	 <table border="1" width="100%">
	 
	 <tr>
	 <td>消息编号</td>
	 <td>消息名称</td>
	 <td>消息内容</td>
	 <td>删除</td>
	 
	 </tr>
	 	<c:forEach var="message" items="${allMessages}">
	 		<tr>
	 			 <td>${message.mid}</td>
				 <td>${message.title}</td>
				 <td>${message.content}</td>
				 <td><a href="pages/back/message/message_delete.action?mid=${message.mid }">删除消息</td>
	 		</tr>
	 	
	 	</c:forEach>
	 </table>
</body>
</html>

        现在假设几种情况,列表可以所有人看,但是删除只能够由固定的人删除

固定信息验证

        一般情况下如果要进行登陆验证往往都会利用数据库,但是考虑到数据库的开发有一些要求,随意本处先使用固定的用户名和密码进行验证,为了方便现在准备出两个用户:

        管理员用户:admin/hello: ROLE_ADMIN,ROLE_USER;

        普通用户:zwb/java:ROLE_USER

      只要是进行登录的检验,几乎都是依靠这过滤器完成的,所以必须在项目的web.xml文件里面配置一个专门的验证过滤器:org.springframework.web.filter.DelegatingFilterProxy

范例:在web.xml文件里面定义授权验证器

        这个过滤器的名字必须是springSecurityFilterChain

  <filter>
  	<filter-name>springSecurityFilterChain</filter-name>
 	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>
  <filter-mapping>
  	<filter-name>springSecurityFilterChain</filter-name>
  		<url-pattern>/*</url-pattern>
  </filter-mapping>

        此处只是启动了一个验证的过滤器,但是具体要验证哪些内容还没有定义.

范例:在applicationContextxml文件里面配置用户相关角色

    导入安全验证的命名空间


xmlns:security="http://www.springframework.org/schema/security"
         定义Spring的安全框架的相关配置:
		<!-- 启动Spring的安全验证 -->
		<security:global-method-security jsr250-annotations="enabled" secured-annotations="enabled"/>
		<!-- 使用默认的安全配置操作,如果出现了授权访问错误,则跳转到403.jsp -->
		<security:http auto-config="true" access-denied-page="/403.jsp"/>
		<!-- 配置授权管理器,以后即便使用了数据库的验证,此处也要配置授权管理器 -->
		<security:authentication-manager  alias="authenticationManager">
			<!-- 定义用户的基本权限信息,本处使用的是固定信息 -->
			<security:authentication-provider>
				<!-- 创建固定的用户名与密码验证 -->
				<security:user-service>
					<security:user name="admin" password="123456" authorities="ROLE_ADMIN,ROLE_USER"/>
					<security:user name="zwb" password="java" authorities="ROLE_USER"/>
				</security:user-service>
			</security:authentication-provider>
			
		</security:authentication-manager>
        此时准备处理相关的用户以及安全框架的自动配置,但是所有的配置最终生效还需要在Action里面修改

范例:修改MessageAction程序类,增加相关的权限配置

package cn.zwb.action;

import java.util.ArrayList;
import java.util.List;

import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import cn.zwb.vo.Message;

@Controller
@RequestMapping("/pages/back/message/*")
public class MessageAction {
	@RequestMapping("message_insertpre")
	@Secured(value={"ROLE_ADMIN"})
	public ModelAndView insertPre(){
		ModelAndView mav=new ModelAndView("message/message_insert");
		return mav;
	}
	@RequestMapping("message_insert")
	@Secured(value={"ROLE_ADMIN"})
	public ModelAndView insert(Message msg){
		System.out.println("[添加数据]"+msg);
		ModelAndView mav =new ModelAndView("forward");
		mav.addObject("msg","信息添加成功!");
		mav.addObject("url","pages/back/message/message_insertpre.action");
		return mav;
	}
	@RequestMapping("message_list")
	@Secured(value={"ROLE_ADMIN","ROLE_USER"})
	public ModelAndView list(){
		ModelAndView mav=new ModelAndView("message/message_list");
		List<Message> all=new ArrayList<Message>();
		for (int i = 0; i < 10; i++) {
			Message msg=new Message();
			msg.setMid(i);
			msg.setTitle("mldn-"+i);
			msg.setContent("www.baidu.com");
			all.add(msg);
		}
			mav.addObject("allMessages",all);
		return mav;
	}
	@RequestMapping("message_delete")
	@Secured(value={"ROLE_ADMIN"})
	public ModelAndView delete (Integer mid){
		System.out.println("[删除数据]数据编号为:"+mid);
		ModelAndView mav =new ModelAndView("forward");
		mav.addObject("msg","信息删除成功!");
		mav.addObject("url","/pages/back/message/message_list.action");
		return mav;
	}
}

        随后在进行调用过程之中,会自动根据用户的角色来进行操作访问.

        在配置成功之后,如果用户没有登录的前提下,那么直接访问授权路径时就会自动跳转到一个登录页面,而此时的路径名称:spring_security_login.包括这个程序的页面都是由Spring自己提供的.登录的检查页面j_spring_security_check,登录的注销页面:j_spring_security_logout.


        现在经过一系列的验证之后,的确可以发现,Spring的安全框架在登录的处理上的确很方便.但是使用的过程里面会存在有一些问题

        很多时候可能需要session中保存的这个用户id信息;

        既然删除操作只有管理可以进行,那么如果是普通用户不应该出现删除的连接

范例:在Action中取出用户名

        用户信息:org.springframework.security.core.userdetails.UserDetails

	@RequestMapping("message_list")
	@Secured(value={"ROLE_ADMIN","ROLE_USER"})
	public ModelAndView list(){
		ModelAndView mav=new ModelAndView("message/message_list");
		UserDetails userdetails=(UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		System.out.println("[当前用户]:"+userdetails.getUsername());
		System.out.println("[登录密码]:"+userdetails.getPassword());
		System.out.println("[用户角色]:"+userdetails.getAuthorities());
		List<Message> all=new ArrayList<Message>();
		for (int i = 0; i < 10; i++) {
			Message msg=new Message();
			msg.setMid(i);
			msg.setTitle("mldn-"+i);
			msg.setContent("www.baidu.com");
			all.add(msg);
		}
			mav.addObject("allMessages",all);
		return mav;
	}

        当访问需要登录之后的页面,没有登录的话会跳转到登录页面,登录之后会重新跳转的要操作的页面

范例:在JSP上取得用户名

<h1>欢迎:${sessionScope['SPRING_SECURITY_CONTEXT'].authentication.principal.username}登录</h1>
        以上只是取得了用户登录的基本信息,而实际上也可以在页面上取得相应的角色信息,但是这个时候就需要使用到Spring特定的标签完成了

范例:在页面中输出角色信息

        定义要使用的安全标签:

<%@taglib prefix="security" uri="http://www.springframework.org/security/tags" %>

        输出所有的角色信息:

<h2>
		<security:authentication property="authorities" var="aut"/>
		${aut}
</h2>

        所有的角色都会以集合的形式返回.

        对于删除操作:是需要授权验证的,如果是管理员可以进行删除,如果是普通用户不应该出现删除


	 <security:authorize ifAnyGranted="ROLE_ADMIN">
	 <td>删除</td>
	 </security:authorize>

  <security:authorize ifAnyGranted="ROLE_ADMIN">
				 <td><a href="pages/back/message/message_delete.action?mid=${message.mid }">删除消息</td>
				 </security:authorize>
        整个Spring的安全框架虽然简单,虽然感觉上方便,但是自动配置的集成度有点太高了

扩展注销功能

        现在已经成功实现Spring自己管理的登录操作包括注销的实现,但是很多时候对于前台页面的要求是很高的,

你不能说直接给一个素颜的页面.往往需要在项目里面定义属于自己的漂流的登录页面,

范例:定义一个专门的登录页

 <body>
   			<form action="<%=basePath %>login" method="post">
   				用户名:<input type="text" name="j_username" id="j_username"><br>
   				密  码:<input type="password" name="j_password" id="j_password"><br>
   				<input type="submit" value="登录">
   			</form>
  </body>

        随后需要手工的来进行登录的处理操作.

范例:修改applicationContext.xml文件使用手工配置

<security:http  access-denied-page="/403.jsp">
			<security:form-login 
			login-page="/login.jsp"    
			login-processing-url="/login"
			default-target-url="/pages/back/message/message_list.action"
			authentication-failure-url="/login?error=true"/>
			<security:logout logout-success-url="/login.jsp"
				delete-cookies="JSESSIONID"
			/>
<security:http  access-denied-page="/403.jsp">
			<security:form-login   
			login-page="/login.jsp"    登录页面的路径
			login-processing-url="/login"    登录页面的提交
			default-target-url="/pages/back/message/message_list.action" 首页
			authentication-failure-url="/login?error=true"/>当登录出错后跳转的页面
			<security:logout logout-success-url="/login.jsp"  注销后跳转的页面
				delete-cookies="JSESSIONID"            注销后清除的COOKIE
			/>
范例:在message_jsp中增加注销连接
<a href="<%=basePath %>j_spring_security_logout">注销</a>

如果登录错误,则直接在login.jsp页面上判断是否存在有error参数以确定提示信息.


<h1>${param.error==true?"登录失败,错误的用户名或密码":""}</h1>

        此时可以由用户自己来决定登录页面.

Session管理

        在一个登录系统之中必须有一个前提:一个账户只能够由一个人登录,那么在Spring安全框架之中就提供有此类的验证方式.也就是说利用一些配置就可以针对于当前Session进行管理

范例:在Web.xml文件里面配置监听器

org.springframework.security.web.session.HttpSessionEventPublisher

  <listener>
  	<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
  </listener>

        此时用户登录之后就会对用户的信息进行保存,而且会保存在application属性之中,如果一旦出现了重复,那么就会通知之前的session注销.

范例:applicationContext文件里面设置Session管理

		<!-- 配置Session的管理操作,如果Session失效跳转到login.jsp上去 -->
		<security:session-management invalid-session-url="/login.jsp?ses=invalid">
			<security:concurrency-control 
					max-sessions="1" 
					error-if-maximum-exceeded="false"
					expired-url="/login.jsp?session=destory"/>
		</security:session-management>
        如果现在用户出现了重复登录(最大的session保存只能够是1,就表示每个session只能保存一次).会跳转到login.jsp页面上.


记住密码

        <>================



DWQ

猜你喜欢

转载自blog.csdn.net/qq1019648709/article/details/80946907