一.前言
前面三篇介绍了security,磨刀不误砍柴工,现在就开始上代码吧。本文的项目demo地址可以在文章尾部看到。
二.DEMO
1.web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/resources/spring-security.xml</param-value>
</context-param>
<!-- Spring 刷新Introspector防止内存泄露 -->
<listener>
<listener-class>
org.springframework.web.util.IntrospectorCleanupListener
</listener-class>
</listener>
<!-- 配置log4j,在这里配置可以动态的改变log4j里面的内容,从而不用重启服务器就可以让新样式生效 -->
<listener>
<listener-class>
org.springframework.web.util.Log4jConfigListener
</listener-class>
</listener>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:/resources/log4j.properties</param-value>
</context-param>
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>6000</param-value>
</context-param>
<!-- session会话管理-->
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
<!-- session超时定义,单位为分钟 -->
<session-config>
<session-timeout>1</session-timeout>
</session-config>
<!-- 编码过滤器 -->
<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>
<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>
</web-app>
2.spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- 对登录页面不进行拦截,否则会进入死循环 -->
<http pattern="/login.jsp" security="none"/>
<http auto-config="true" access-decision-manager-ref="accessDecisionManager"><!-- access-decision-manager-ref涉及到角色继承 -->
<form-login login-page="/login.jsp" default-target-url="/index.jsp" authentication-failure-url="/login.jsp?error=1" />
<!-- 尝试访问没有权限的页面时跳转的页面 -->
<access-denied-handler error-page="/accessDeny.jsp"/>
<intercept-url pattern="/**" access="ROLE_USER"/>
<!-- 注销成功跳转到登录页面 -->
<logout logout-success-url="/login.jsp" logout-url="/j_spring_security_logout" invalidate-session="true" delete-cookies="JSESSIONID"/>
<!-- 同步会话管理,设置允许同一个账户登录2次 ,一般都是设置1次-->
<session-management invalid-session-url="/timeout.jsp" > <!-- invalid-session-url设置连接超时跳转页面 -->
<!-- error-if-maximum-exceeded="true":超出最大连接数以后,false-后登陆的将先登录的挤出系统,true: 后面的用户禁止登陆 -->
<concurrency-control max-sessions="2" error-if-maximum-exceeded="true" />
</session-management>
<!-- 防止session攻击,在用户登录时销毁用户当前session,并生成新session,但不会复制任何原有属性 -->
<session-management session-fixation-protection="newSession"/>
</http>
<!-- 验证配置 , 认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->
<authentication-manager>
<authentication-provider user-service-ref="myUserDetailsService">
<!-- <authentication-provider>
<user-service>
<user name="zhou" password="123" authorities="ROLE_ADMIN"/>
</user-service>
-->
<!-- 使用用户名作为盐值 -->
<!-- <password-encoder hash="md5">
<salt-source user-property="username" />
</password-encoder> -->
</authentication-provider>
</authentication-manager>
<!-- 启用角色继承功能,ROLE_ADMIN > ROLE_USER表示只要是允许ROLE_USER访问的资源,
ROLE_ADMIN也都有权限进行访问,去掉START和END之间的代码一样是能正常运行的 ,
但是因为在MyUserDetialsService类中authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
我只分配了他管理员角色,所以会跳转到accessDeny.jsp。
去掉角色继承功能时记得除去<http access-decision-manager-ref="accessDecisionManager">属性-->
<!-- START -->
<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<beans:property name="allowIfAllAbstainDecisions" value="false"/>
<beans:property name="decisionVoters">
<beans:list>
<beans:ref bean="roleHierarchyVoter"/>
<beans:ref bean="authenticatedVoter"/>
</beans:list>
</beans:property>
</beans:bean>
<beans:bean id="roleHierarchyVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
<beans:constructor-arg ref="roleHierarchy"/>
</beans:bean>
<beans:bean id="authenticatedVoter" class="org.springframework.security.access.vote.AuthenticatedVoter"/>
<beans:bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<!-- <beans:property name="hierarchy" value="ROLE_ADMIN > ROLE_USER"/> -->
<!-- 如果希望配置更多继承关系,可以使用换行进行分隔 -->
<beans:property name="hierarchy">
<beans:value>
ROLE_ADMIN > ROLE_USER
ROLE_MANAGER > ROLE_USER
</beans:value>
</beans:property>
</beans:bean>
<!-- END -->
<beans:bean id="myUserDetailsService" class="com.zsj.MyUserDetialsService" />
<!-- 定义国际化,支持中文异常信息 -->
<beans:bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basename"
value="classpath*:/messages_zh_CN"/>
</beans:bean>
</beans:beans>
3.UserDetailsService
package com.zsj;
import java.io.IOException;
import java.io.Reader;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
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 MyUserDetialsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
// TODO Auto-generated method stub
UserDetails userDetails=null;
UserInfo user=null;
try {
user=(UserInfo) findByName(name);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(user==null){
throw new UsernameNotFoundException("该用户并不存在");
}else{
Collection<GrantedAuthority> authorities = new ArrayList<>();
//这里我就直接赋值ROLE_ADMIN了,真正项目是要从数据库获取角色
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
userDetails=new User(user.getUserName(),user.getPassword()+"",authorities);
System.out.println(userDetails);
}
return userDetails;
}
//为简化项目,直接上恶心的原始mybatis吧,不和spring集成了,这里也可以换成你设计的查找用户的DAO。
public UserInfo findByName(String name) throws IOException{
String config="resources/mybatis-config.xml";
Reader reader=Resources.getResourceAsReader(config);//Resources 类为从类路径中加载资源
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
/**
* 映射sql的标识字符串,
* com.dao.DeptDao是deptMapper.xml文件中mapper标签的namespace属性的值,
* findByDeptNum是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
*/
String statement = "com.zsj.UserDao.findByUserName";//请注意这里是点分,不是正斜杠
String param = "zhou";
UserInfo user = session.selectOne(statement, param);//执行DeptDao中的findByDeptNum方法,方法参数为"D001"
System.out.println(user.getPassword());
return user;
}
}
4.login.jsp
<%@page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>登录</title>
</head>
<body>
<!--特别强调,请注意红色字体!!!!!!!!-->
<form action="<span style="color:#FF0000;">j_spring_security_check</span>" method="POST">
<table>
<tr>
<td>用户:</td>
<td><input type='text' name=<span style="color:#FF0000;">'j_username</span>'></td>
</tr>
<tr>
<td>密码:</td>
<td><input type='password' name='<span style="color:#FF0000;">j_password</span>'></td>
</tr>
<tr>
<td>记住我<input id="_spring_security_remember_me" name="_spring_security_remember_me" type="checkbox" value="true"/></td>
<td><input name="reset" type="reset"></td>
<td><input name="submit" type="submit"></td>
</tr>
</table>
</form>
</body>
</html>
5.运作流程
(1).网址输入http://localhost:8080/TestSecurity
(2)由于security拦截器拦截作用,改请求会被拦截,由于没有登录认证,会被定向到登录页面
(3)登录页面输入账号密码,请求跳转到主页,该请求又被拦截,会拦截器会调用认证管理器,认证供应器来认证用户,认证成功则接着验证授权,认证和授权都通过才可以进入主页
6.结尾
如需项目DEMO的源码,请到下面地址下载:http://download.csdn.net/detail/u012557538/9373827
学习博客:
http://haohaoxuexi.iteye.com/blog/2157769 http://wiki.jikexueyuan.com/project/spring-security/authenticationProvider.html http://www.mossle.com/docs/auth/html/index.html