SpringSecurity整合SSH的简单例子
SpringSecurity也研究好几天了,了解了一些简单实用的功能,来做个记录,方便自己回顾,方便大家
借鉴。
目录结构如下
一、导包。pom.xml片段如下:
<properties>
<spring>4.1.6.RELEASE</spring>
<hibernate>4.3.9.FINAL</hibernate>
<struts2>2.3.20</struts2>
<spring-security>3.2.7.RELEASE</spring-security>
</properties>
<dependencies>
<!-- <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.12</version>
</dependency> -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.3</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring-security}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring-security}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring-security}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>${struts2}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${hibernate}</version>
</dependency>
</dependencies>
用的是maven管理,不用的话就按照版本一点点的复制过来。
有些包可能没用上,不过暂时就扔着,懒得删了。
二、配置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">
<!-- 读取spring配置文件 -->
<!-- 必须将spring基本配置和SpringSecurity配置全部读取 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-security.xml,classpath:spring-
common.xml</param-value>
</context-param>
<!-- 设置SpringSecurity的 Filter -->
<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>
<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置 Struts2 的 Filter -->
<filter>
<filter-name>struts2</filter-name>
<filter-
class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
这里如果用springmvc的话会出现很多问题,但是大部分都是配置文件读取顺序问题,建议将通用的
spring配置提取出来,放到最上面首先读取一遍。
三、applicationContext.xml(我这里取名叫spring-common了)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!-- 开启注解 -->
<context:annotation-config />
<!-- 实体bean扫描路径 -->
<context:component-scan base-package="com" />
<!-- 导入资源文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 配置 C3P0 数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
</bean>
<!-- 配置 HibernateSessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation"
value="classpath:hibernate.cfg.xml"></property>
<property name="mappingLocations"
value="classpath:com/exp/entities/*.hbm.xml"></property>
<property name="hibernateProperties">
<props>
<prop key="dialect">${dialect}</prop>
</props>
</property>
</bean>
<!--配置 Spring 的声明式事务 1. 配置 hibernate 的事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- @Transactional注解扫描 -->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
四、资源文件db.properties
#mysql
jdbc.user=root
jdbc.password=123456
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///test?useUnicode=true&characterEncoding=utf-8
dialect=org.hibernate.dialect.MySQLDialect
jdbc.initPoolSize=5
jdbc.maxPoolSize=10
五、hibernate.cfg.xml(可以提取到spring-common中)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>
六、struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<!-- 关闭动态方法调用 -->
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<!-- 关闭开发者模式(不推荐) -->
<constant name="struts.devMode" value="false" />
<!-- 将jsp页面的s标签默认样式取消 -->
<constant name="struts.ui.theme" value="simple" />
<package name="default" namespace="/" extends="struts-default">
<action name="index">
<result>/index.jsp</result>
</action>
<action name="user_*" class="userAction" method="{1}">
<result name="list">/views/user/list.jsp</result>
</action>
</package>
</struts>
七、log4j.properties
log4j.rootLogger=INFO, CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
八、spring-security.xml
<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.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!-- 如自定义登录页面必须将之权限需求设为空,否则将出现死循环 -->
<http pattern="/views/login.jsp" security="none" />
<!--
1.access-denied-page:权限不足时跳转页面 ( ref属性可以设置跳转action )
2.use-expressions="true":开启表达式(推荐) PS:开启后非JAVA为文件写法
为:hasRole('ROLE_USER')-->
<http use-expressions="true" access-denied-page="/views/roleError.jsp">
<!-- 过滤地址
pattern:路径
access:权限 -->
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<!-- 登录配置
1.login-processing-url:登录form中action的地址(默认
为/j_spring_security_check)
2.username-parameter:登录form中用户名的username(默认为/j_username)
3.password-parameter:登录form中用户名的password(默认为/j_password)
4.login-page:登录页(将覆盖默认登录页)
5.default-target-url:默认登录后跳转的页面(如果登录前没有指定跳转页
面将生效)
6.authentication-failure-url:错误页
7.always-use-default-target="true":登录后强制跳转到5所指定的页面
-->
<form-login
login-processing-url="/login"
username-parameter="username"
password-parameter="password"
login-page="/views/login.jsp"
default-target-url="/index.jsp"
authentication-failure-url="/views/login.jsp?error=true"
always-use-default-target="true" />
<!-- 注销配置 1.logout-success-url:注销后跳转页面 -->
<logout logout-success-url="/views/login.jsp?logout=true" />
</http>
<!-- 使用jsr250注解保护方法 (例:com.exp.service.UserService.findAll) -->
<global-method-security jsr250-annotations="enabled" />
<authentication-manager>
<!-- 手动配置权限列表。
PS: 1.实体bean必须实现 UserDetails接口,否则无法转换成正确类型
2.myUserDetailService该bean必须实现UserDetailsService接口并写好对应
方法返回UserDetails类型-->
<authentication-provider user-service-ref='myUserDetailService' />
</authentication-manager>
</beans:beans>
九、实体类User.java以及映射文件User.hbm.xml(个人习惯xml,也可以换成注解)
User.java
package com.exp.entities;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
//UserDetails实现该接口后将可以转换成UserDetails类型
public class User implements UserDetails {
/**
*
*/
private static final long serialVersionUID = -2790587574312673083L;
private Integer id;
private String username;
private String password;
private String role;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password="
+ password + ", role=" + role + "]";
}
public User(Integer id, String username, String password, String role) {
super();
this.id = id;
this.username = username;
this.password = password;
this.role = role;
}
public User() {
super();
}
//如role为list或其余类型,可以做循环依次放入gas中
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
List<GrantedAuthority> gas = new ArrayList<GrantedAuthority>();
GrantedAuthority ga = new SimpleGrantedAuthority(role);
gas.add(ga);
return gas;
}
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
public boolean isEnabled() {
// TODO Auto-generated method stub
return true;
}
public User(String username, String password, String role) {
super();
this.username = username;
this.password = password;
this.role = role;
}
}
User.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2015-4-28 11:54:11 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
<class name="com.exp.entities.User" table="T_USER">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<property name="username" type="java.lang.String">
<column name="USERNAME" />
</property>
<property name="password" type="java.lang.String">
<column name="PASSWORD" />
</property>
<property name="role" type="java.lang.String">
<column name="ROLE" />
</property>
</class>
</hibernate-mapping>
十、公共类。用来提取代码。
BaseDao.java
package com.exp.base;
import java.util.List;
import org.springframework.security.core.userdetails.UserDetails;
public interface BaseDao<T> {
public UserDetails findByUsername(String username);
public List<T> findAll();
}
BaseDaoImpl.java
package com.exp.base;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import javax.annotation.Resource;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.exp.entities.User;
@Transactional
@SuppressWarnings("unchecked")
public class BaseDaoImpl<T> implements BaseDao<T> {
private Class<T> clazz;
public BaseDaoImpl() {
ParameterizedType pt = (ParameterizedType) this.getClass()
.getGenericSuperclass();
this.clazz = (Class<T>) pt.getActualTypeArguments()[0];
}
@Resource
private SessionFactory sessionFactory;
public Session getSession() {
return sessionFactory.getCurrentSession();
}
public UserDetails findByUsername(String username) {
User user = (User) getSession()
.createQuery("from User u where u.username=:username")
.setParameter("username", username).uniqueResult();
return (UserDetails) user;
}
public List<T> findAll() {
return getSession().createQuery("from " + clazz.getSimpleName()).list();
}
}
之后还会有BaseAction暂时先不管
十一、action
UserAction.java
package com.exp.actions;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.context.annotation.Scope;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import com.exp.service.UserService;
import com.opensymphony.xwork2.ActionContext;
@Controller
@Scope("prototype")
public class UserAction{
private static final String list = "list";
@Resource
private UserService userService;
public String list(){
List list1 = userService.findAll();
ActionContext.getContext().put("list", list1);
return list;
}
}
十二、service
UserService.java
package com.exp.service;
import java.util.List;
import javax.annotation.security.RolesAllowed;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import com.exp.base.BaseDao;
import com.exp.entities.User;
public interface UserService extends BaseDao<User>{
@RolesAllowed("ROLE_USER")
List<User> findAll();
}
UserServiceImpl.java
package com.exp.service.impl;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.exp.base.BaseDaoImpl;
import com.exp.entities.User;
import com.exp.service.UserService;
@Service
@Transactional
public class UserServiceImpl extends BaseDaoImpl<User> implements UserService {
public List<User> findAll(){
return getSession().createQuery("from User").list();
}
}
十三、security部分java
MyUserDetailService.java
package com.exp.security;
import javax.annotation.Resource;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import com.exp.base.BaseDao;
import com.exp.base.BaseDaoImpl;
import com.exp.entities.User;
@Component
public class MyUserDetailService extends BaseDaoImpl<User> implements UserDetailsService{
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
// TODO Auto-generated method stub
//用hibernate查询user表role字段。关联也可以。
//可以返回User类型之后在转换成UserDetails类型ps:实体bean必须实现UserDetails
接口,否则无法转换
UserDetails user = findByUsername(username);
return user;
}
}
十四、jsp页面
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<base href="<%=basePath%>">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>Hello World!</h2>
<a href="j_spring_security_logout">logout</a>
<a href="user_list.action">用户列表</a>
</body>
</html>
/views/login.jsp
<h1>Login</h1>
<form name='f' action="<%=basePath%>login"
method='POST'>
<s:if test="%{#parameters.error != null}">
<p>请确认您的密码</p>
</s:if>
<s:if test="%{#parameters.logout != null}">
<p>您已经成功注销</p>
</s:if>
<table>
<tr>
<td>User:</td>
<td><input type='text' name='username' value=''></td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' name='password' /></td>
</tr>
<tr>
<td><input name="submit" type="submit" value="submit"
/></td>
</tr>
</table>
</form>
/views/roleError.jsp
Role Error(BODY中就这几个字)
/views/user/list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<%@ taglib prefix="sec"
uri="http://www.springframework.org/security/tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<table border="1">
<tr>
<td>用户名</td>
<td>密码</td>
<td>权限</td>
</tr>
<s:iterator value="list">
<tr>
<td><s:property value="username" /></td>
<td><s:property value="password" /></td>
<td><s:property value="role" /></td>
</tr>
</s:iterator>
</table>
<hr>
<sec:authorize access="hasRole('ROLE_ADMIN')">
<h1>ADMIN</h1>
</sec:authorize>
<sec:authorize access="hasRole('ROLE_USER')">
<h1>USER</h1>
</sec:authorize>
<a href="index">返回首页</a>
</body>
</html>
十五、来个测试类
ffff.java
package com.tttt;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import net.sf.json.JSONObject;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-common.xml")
public class ffff {
@Resource
private SessionFactory sessionFactory;
public Session getSession(){
return sessionFactory.getCurrentSession();
}
@Test
public void a1(){
String name="abc";
String password="123";
Map map = new HashMap();
map.put("name", name);
map.put("password", password);
JSONObject jsonObject = JSONObject.fromObject(map);
System.out.println(jsonObject);
}
@Test
@Transactional
public void a2(){
List list = getSession().createQuery("from User").list();
System.out.println(list);
}
}
十六、总结
SpringSecurity在熟练后会很便捷的、快速的完成原本繁重的、伤脑筋的权限模块。
目前例子中实现了对url的保护、对方法的保护、以及对页面的保护。
其余还有更多的功能等待你我的深入研究。
欢迎评论指正