Author: Kagula
Date: 2016-9-19
环境
[1]Spring 3.1.2
[2]Tomcat 7.x
概要
在《Spring Security学习一》的基础上完善自定义Login界面,并增加了Login方法的自定义。
这里仅列出源码。
测试内容
[1]能否把密码明文转成密文,MD5加密。pass.
[2]能否同Spring MVC兼容。pass.
[3]自定义login方法。pass.
[4]个性化提示用户没有权限。pass.
[5]排除对jpg等图片的权限检查。pass.
[6]增加一组URL的权限检查。pass.
[7]自定义404错误。pass.
正文
第一部份配置文件
文档结构如图
4.0.0
com.nuoke
testSpringSecurity2
war
0.0.1-SNAPSHOT
testSpringSecurity2 Maven Webapp
http://maven.apache.org
UTF-8
UTF-8
3.1.2.RELEASE
org.springframework
spring-core
${spring.version}
org.springframework
spring-beans
${spring.version}
org.springframework
spring-webmvc
${spring.version}
org.springframework.security
spring-security-core
${spring.version}
org.springframework.security
spring-security-web
${spring.version}
org.springframework.security
spring-security-config
${spring.version}
org.springframework.security
spring-security-taglibs
${spring.version}
jstl
jstl
1.2
testSpringSecurity2
org.apache.maven.plugins
maven-compiler-plugin
3.0
1.7
web.xml
Archetype Created Web Application
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
encodingFilter
/*
contextConfigLocation
/WEB-INF/spring-servlet.xml,/WEB-INF/spring-security.xml
org.springframework.web.context.ContextLoaderListener
org.springframework.security.web.session.HttpSessionEventPublisher
springSecurityFilterChain
org.springframework.web.filter.DelegatingFilterProxy
springSecurityFilterChain
/*
spring
org.springframework.web.servlet.DispatcherServlet
1
spring
*.do
index.jsp
404
/My404.jsp
java.lang.Exception
/MyEception.jsp
spring-servlet.xml
outException
outException
spring-security.xml
第二部份java源文件
共有7个class文件,这里只列出《学习一》内容不同或没有的java源文件
package com.nuoke;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class MyUserDetailService implements UserDetailsService {
//登陆验证时,通过username获取用户的所有权限信息,
//并返回User放到spring的全局缓存SecurityContextHolder中,以供授权器使用
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
Collection auths=new ArrayList();
SimpleGrantedAuthority auth2 = new SimpleGrantedAuthority("ROLE_ADMIN");
SimpleGrantedAuthority auth1 = new SimpleGrantedAuthority("ROLE_USER");
if(username.equals("admin")){
auths=new ArrayList();
auths.add(auth1);
auths.add(auth2);
}
//第二个参数是密码。是123的md5码。
User user = new User(username, "202cb962ac59075b964b07152d234b70", true, true, true, true, auths);
//以后还可以new a class derived from User class,为user存放更多有关这个user的信息。
//参考下文User定义,可以存放用户的更多属性。
//http://blog.csdn.net/ydj7501603/article/details/9049663
return user;
}
}
MyController.java
package com.nuoke.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping(value = "/main")
public class MyController {
@RequestMapping(value = "/admin.do")
public ModelAndView adminPage() {
ModelAndView model = new ModelAndView();
model.addObject("title", "Spring Security Hello World");
model.addObject("message", "这是一个安全被保护的页面!");
//在MyInvocationSecurityMetadataSource类中指定了保护。
model.setViewName("admin");
return model;
}
@RequestMapping(value = "/welcome.do")
public ModelAndView WelcomeAction() {
ModelAndView model = new ModelAndView();
model.addObject("title", "Spring Security Hello World");
model.addObject("message", "这是一个欢迎页面!");
model.setViewName("welcome");
return model;
}
/*
* 仅自定义login页面
* 测试url:
* http://localhost:8080/testSpringSecurity2/main/login.do
*/
@RequestMapping(value = "/login.do")
public ModelAndView LoginAction(
@RequestParam(value = "error", required = false) String error,
@RequestParam(value = "logout", required = false) String logout) {
ModelAndView model = new ModelAndView();
if (error != null) {
model.addObject("error", "用户名或密码不正确!");
}
if (logout != null) {
model.addObject("msg", "您已成功注销系统.");
}
model.setViewName("login");
return model;
}
//自定义Login方法示例
/*
* 测试Url:
* http://localhost:8080/testSpringSecurity2/main/customLogin.do
*
* 补充参考资料:
* 《在spring security手动 自定义 用户认证 SecurityContextHolder》
* https://my.oschina.net/lemonzone2010/blog/268452#OSC_h2_1
* 《》
* http://www.programcreek.com/java-api-examples/index.php?api=org.springframework.security.core.context.SecurityContext
*/
@Autowired
private AuthenticationManager authenticationManager;
@RequestMapping(value="/customLogin.do")
public ModelAndView customLoginAction(@RequestParam(defaultValue="") String username,
@RequestParam(defaultValue="") String password,
HttpServletRequest request){
//
username = username.trim();
//返回登录页面
ModelAndView model = new ModelAndView();
model.setViewName("customLogin");
if(username==null||username.isEmpty()||
password==null||password.isEmpty())
{
return model;
}
//向AJAX请求返回消息提醒(json字符串)
model.setViewName("customLoginResponse");
UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(username, password);
//
try {
Authentication authentication = authenticationManager.authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(authentication);
HttpSession session = request.getSession();
session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext()); // 这个非常重要,否则验证后将无法登陆
model.addObject("message","登录用户:"+authentication.getName());
model.addObject("ok",1);//这样view/customLogin.jsp得到成功标记后可以做url跳转。
} catch (AuthenticationException ex) {
model.addObject("message","用户名或密码错误");
model.addObject("ok",0);//为了view/customLogin.jsp得到失败标记后可以提醒用户重新输入用户名、密码。
}//end catch
return model;
}//end handler
}//end class
第三部份jsp源文件
参考图一共有十个jsp文件,这里只列出比较重要的几个jsp。
MyException.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
异常处理页面
<% Exception ex = (Exception) request.getAttribute("Exception");%>
Exception:<%=ex.getMessage()%>
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
示例一:自定义登录界面
请输入您的用户名与密码
${error}
${msg}
welcome.jsp
<%@ page language="java" import="java.util.*,java.text.*" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page session="false"%>
标题 : ${title}
消息 : ${message}
<%
Date date = new Date();
SimpleDateFormat t = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = t.format(date);
%>
当前时间:<%= time %>
admin.jsp
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page session="true"%>
标题 : ${title}
消息 : ${message}
欢迎 : ${pageContext.request.userPrincipal.name} 登录本系统 | 注销
customLogin.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
自定义登录控制
示例二 自定义login方法
customLoginResponse.jsp
<%@ page language="java" import="java.util.*,java.text.*" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page session="false"%>
{"ok":${ok},"msg":"${message}"}
遗留问题
登录的时候,如何知道帐户已经在其它地方登录?
参考资料
[1]《Spring Security》
http://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity.html
[2]《intercept-url配置》
参考这篇文章可以限定用户只能使用https协议访问服务
http://haohaoxuexi.iteye.com/blog/2161056
[3]《Spring Security 3.1.2 + Spring Framework 3.1.2+使用Annotation实战指南》
http://www.itpub.net/thread-1719846-1-1.html
[4]《Spring Security 3.1 自定义实例之登陆》
使用AJAX来验证登录
使用了spring自带的login方法来验证登录
http://www.cnblogs.com/ilife/archive/2013/02/28/2936067.html
[5]《Spring Security 3.1.2.RELEASE API》
http://tool.oschina.net/uploads/apidocs/spring-security-3.1.2/apidocs/
[6]《spring security Controller用户角色的判断》
http://blog.csdn.net/softwarehe/article/details/7711302
[7]《How to manually log out a user with spring security?》
http://stackoverflow.com/questions/5727380/how-to-manually-log-out-a-user-with-spring-security
[8]《Spring and Angular JS: A Secure Single Page Application》
https://spring.io/blog/2015/01/12/spring-and-angular-js-a-secure-single-page-application