Spring Security入门Demo

一、Spring Security简介

SpringSecurity,这是一种基于Spring AOP和Servlet过滤器的安全框架。它提供全面的安全性解决方案,同时在Web请求级和方法调用级处理身份确认和授权。在Spring Framework基础上,Spring Security充分利用了依赖注入(DI,Dependency Injection)和面向切面技术。

 

二、建立工程

参考http://blog.csdn.net/haishu_zheng/article/details/51490299,用第二种方法创建名为spring-security-demo的Maven工程。

工程的最终目录结构为


 

三、源代码

1 pom.xml里引入所需要的包

<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>spring-security-demo</groupId>
  <artifactId>spring-security-demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  <name>spring-security-demo</name>
  <description/>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.2.9.RELEASE</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.2.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>3.1.6.RELEASE</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>3.1.6.RELEASE</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.15</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
    </dependencies>
 
</project>


2 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>spring-security-demo</display-name>
	<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>

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
		/WEB-INF/spring-security.xml
		/WEB-INF/applicationContext.xml
		</param-value>
	</context-param>

	<servlet>
		<servlet-name>spring</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>spring</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
</web-app>

这里两处关于springsecurity的配置表示项目中所有路径的资源都要经过Spring Security。

注意:最好是将DelegatingFilterProxy写在DispatcherServlet之前,否则Spring Security可能不会正常工作。

 

3 spring-servlet.xml

<?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:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	   		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<!-- 定义一个视图解析器 -->
	<bean id="viewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver"
		p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />
</beans>


这个XML配置声明一个视图解析器.在控制器中会根据JSP名映射到/WEB-INF/jsp中相应的位置。


4 applicationContext.xml

<?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:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	   		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	   		http://www.springframework.org/schema/context
	   		http://www.springframework.org/schema/context/spring-context-3.0.xsd
			http://www.springframework.org/schema/mvc 
			http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

	<!-- 激活spring的注解. -->
	<context:annotation-config />

	<!-- 扫描注解组件并且自动的注入spring beans中. 
	例如,他会扫描@Controller 和@Service下的文件.所以确保此base-package设置正确. -->
	<context:component-scan base-package="com.demo" />

	<!-- 配置注解驱动的Spring MVC Controller 的编程模型.注:次标签只在 Servlet MVC工作! -->
	<mvc:annotation-driven />

</beans>


5 spring-security.xml

<?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:security="http://www.springframework.org/schema/security"
	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-3.1.xsd">
	
	<!--  Spring-Security 的配置 -->
	<!-- 注意use-expressions=true.表示开启表达式,否则表达式将不可用.
	see:http://www.family168.com/tutorial/springsecurity3/html/el-access.html
	 -->
	<security:http auto-config="true" use-expressions="true" access-denied-page="/auth/denied" >
		
		<security:intercept-url pattern="/auth/login" access="permitAll"/>
		<security:intercept-url pattern="/main/admin" access="hasRole('ROLE_ADMIN')"/>
		<security:intercept-url pattern="/main/common" access="hasRole('ROLE_USER')"/>
		
		<security:form-login
				login-page="/auth/login" 
				authentication-failure-url="/auth/login?error=true" 
				default-target-url="/main/common"/>
			
		<security:logout 
				invalidate-session="true" 
				logout-success-url="/auth/login" 
				logout-url="/auth/logout"/>
	
	</security:http>
	
	<!-- 指定一个自定义的authentication-manager :customUserDetailsService -->
	<security:authentication-manager>
	        <security:authentication-provider user-service-ref="customUserDetailsService">
	        		<security:password-encoder ref="passwordEncoder"/>
	        </security:authentication-provider>
	</security:authentication-manager>
	
	<!-- 对密码进行MD5编码 -->
	<bean class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" id="passwordEncoder"/>

	<!-- 
		通过 customUserDetailsService,Spring会自动的用户的访问级别.
		也可以理解成:以后我们和数据库操作就是通过customUserDetailsService来进行关联.
	 -->
	<bean id="customUserDetailsService" class="com.demo.service.CustomUserDetailsService"/>
	
</beans>

分析:

(一)

这里/auth/login的权限为permitAll,表示所有人都可以访问此页面;/main/admin的权限为ROLE_ADMIN,表示属于ROLE_ADMIN角色的用户才有权访问此页面;/main/common的权限为ROLE_USER,表示属于ROLE_USER的用户才有权访问此页面。

 

需要注意的是我们使用了SpringEL表达式来指定角色的访问.

以下是表达式对应的用法:

hasRole([role])返回 true 如果当前主体拥有特定角色。

hasAnyRole([role1,role2])返回 true 如果当前主体拥有任何一个提供的角色 (使用逗号分隔的字符串队列)

principal 允许直接访问主体对象,表示当前用户

authentication允许直接访问当前 Authentication对象 从SecurityContext中获得

permitAll 一直返回true

denyAll 一直返回false

isAnonymous()如果用户是一个匿名登录的用户 就会返回 true

isRememberMe()如果用户是通过remember-me 登录的用户 就会返回true

isAuthenticated()如果用户不是匿名用户就会返回true

isFullyAuthenticated()如果用户不是通过匿名也不是通过remember-me登录的用户时, 就会返回true。

 

(二)

        <security:form-login
                login-page="/auth/login"
                authentication-failure-url="/auth/login?error=true"
                default-target-url="/main/common"/>
表示通过 /auth/login这个映射进行登录. 

如果验证失败则返回一个URL:/auth/login?error=true
如果登录成功则默认指向:/main/common

 

(三)

<security:logout
                invalidate-session="true"
                logout-success-url="/auth/login"
                logout-url="/auth/logout"/>
这里我们开启了session失效功能。注销URL为:/auth/logout;注销成功后转向:/auth/login。

 

(四)

    <bean id="customUserDetailsService" class="com.demo.service.CustomUserDetailsService"/>

一个自定义的CustomUserDetailsService,是实现SpringSecurity的UserDetailsService接口,但我们重写了他即便于我们进行数据库操作.

 

6 loginpage.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!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>

	<h1>Login</h1>

	<div id="login-error">${error}</div>

	<form action="../j_spring_security_check" method="post">

		<p>
			<label for="j_username">Username</label> <input id="j_username"
				name="j_username" type="text" />
		</p>

		<p>
			<label for="j_password">Password</label> <input id="j_password"
				name="j_password" type="password" />
		</p>

		<input type="submit" value="Login" />

	</form>

</body>
</html>

7 commonpage.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!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>
	<h1>Common Page</h1>
	<p>每个人都能访问的页面.</p>
	<a href="/spring-security-demo/main/admin"> Go AdminPage </a>
	<br />
	<a href="/spring-security-demo/auth/login">退出登录</a>

</body>
</html>

8 adminpage.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!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>
	<h1>Admin Page</h1>
	<p>管理员页面</p>
	<a href="/spring-security-demo/auth/login">退出登录</a>
</body>
</html>

9 deniedpage.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!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>
	<h1>你的权限不够!</h1>
	<p>只有拥有Admin权限才能访问!</p>
	<a href="/spring-security-demo/auth/login">退出登录</a>
</body>
</html>

10 数据模型DbUser.java

package com.demo.domain;

public class DbUser {

	private String username;
	private String password;
	private Integer access;

	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 Integer getAccess() {
		return access;
	}

	public void setAccess(Integer access) {
		this.access = access;
	}

}

11 UserDao.java,通过一个初始化的List来模拟数据库操作

package com.demo.dao;

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

import org.apache.log4j.Logger;
import com.demo.domain.DbUser;

public class UserDao {

	protected static Logger logger = Logger.getLogger("dao");

	public DbUser getDatabase(String username) {

		List<DbUser> users = internalDatabase();

		for (DbUser dbUser : users) {
			if (dbUser.getUsername().equals(username) == true) {
				logger.debug("User found");
				return dbUser;
			}
		}
		logger.error("User does not exist!");
		throw new RuntimeException("User does not exist!");

	}

	/**
	 * 初始化数据
	 */
	private List<DbUser> internalDatabase() {

		List<DbUser> users = new ArrayList<DbUser>();
		DbUser user = null;

		user = new DbUser();
		user.setUsername("admin");

		// "admin"经过MD5加密后
		user.setPassword("21232f297a57a5a743894a0e4a801fc3");
		user.setAccess(1);

		users.add(user);

		user = new DbUser();
		user.setUsername("user");

		// "user"经过MD5加密后
		user.setPassword("ee11cbb19052e40b07aac0ca060c23ee");
		user.setAccess(2);

		users.add(user);

		return users;

	}
}


12 CustomUserDetailsService.java,自定义UserDetailsService,可以通过继承UserDetailsService来达到灵活的自定义UserDetailsService

package com.demo.service;

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

import org.apache.log4j.Logger;
import com.demo.dao.UserDao;
import com.demo.domain.DbUser;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
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;

/**
 * 一个自定义的service用来和数据库进行操作. 即以后我们要通过数据库保存权限.则需要我们继承UserDetailsService
 * 
 */
public class CustomUserDetailsService implements UserDetailsService {

	protected static Logger logger = Logger.getLogger("service");

	private UserDao userDAO = new UserDao();

	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException, DataAccessException {

		UserDetails user = null;

		try {

			// 搜索数据库以匹配用户登录名.
			// 我们可以通过dao使用JDBC来访问数据库
			DbUser dbUser = userDAO.getDatabase(username);

			// Populate the Spring User object with details from the dbUser
			// Here we just pass the username, password, and access level
			// getAuthorities() will translate the access level to the correct
			// role type

			user = new User(dbUser.getUsername(), dbUser.getPassword()
					.toLowerCase(), true, true, true, true,
					getAuthorities(dbUser.getAccess()));

		} catch (Exception e) {
			logger.error("Error in retrieving user");
			throw new UsernameNotFoundException("Error in retrieving user");
		}

		return user;
	}

	/**
	 * 获得访问角色权限
	 * 
	 * @param access
	 * @return
	 */
	public Collection<GrantedAuthority> getAuthorities(Integer access) {

		List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(2);

		// 所有的用户默认拥有ROLE_USER权限
		logger.debug("Grant ROLE_USER to this user");
		authList.add(new GrantedAuthorityImpl("ROLE_USER"));

		// 如果参数access为1.则拥有ROLE_ADMIN权限
		if (access.compareTo(1) == 0) {
			logger.debug("Grant ROLE_ADMIN to this user");
			authList.add(new GrantedAuthorityImpl("ROLE_ADMIN"));
		}

		return authList;
	}
}

13 控制器LoginController.java

package com.demo.controller;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("auth")
public class LoginController {
	protected static Logger logger = Logger.getLogger("controller");

	/**
	 * 指向登录页面
	 */
	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public String getLoginPage(
			@RequestParam(value = "error", required = false) boolean error,
			ModelMap model) {

		logger.debug("Received request to show login page");

		if (error == true) {
			// Assign an error message
			model.put("error",
					"You have entered an invalid username or password!");
		} else {
			model.put("error", "");
		}
		return "loginpage";
	}

	/**
	 * 指定无访问额权限页面
	 * 
	 * @return
	 */
	@RequestMapping(value = "/denied", method = RequestMethod.GET)
	public String getDeniedPage() {

		logger.debug("Received request to show denied page");

		return "deniedpage";
	}
}

该controller有两个mapping映射

main/common

main/admin

现在我们将同过Spring Security框架实现成功登陆的人都能访问到main/common,但只有拥有admin权限的用户才能访问main/admin。

 

14 控制器MainController.java

package com.demo.controller;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/main")
public class MainController {
	protected static Logger logger = Logger.getLogger("controller");

	/**
	 * 跳转到commonpage页面
	 * 
	 * @return
	 */
	@RequestMapping(value = "/common", method = RequestMethod.GET)
	public String getCommonPage() {
		logger.debug("Received request to show common page");
		return "commonpage";
	}

	/**
	 * 跳转到adminpage页面
	 * 
	 * @return
	 */
	@RequestMapping(value = "/admin", method = RequestMethod.GET)
	public String getAadminPage() {
		logger.debug("Received request to show admin page");
		return "adminpage";

	}

}

四、运行结果

1 启动spring-security-demo程序

 

2 在浏览器里输入http://localhost:8080/spring-security-demo/auth/login

 

3 输入用户名admin密码admin后,点击“Login”按纽


 


4 点击“Go AdminPage”链接,因为有权限,所以可看到管理员页面

 

5 点击“退出登录”,返回登录页

 

6 输入用户名user密码user并登录

 


7 点击Go AdminPage链接,因为没有权限,所以看到权限不够的提示

 

8 退出登录,返回登录页

 

五、源码下载地址

CSDN: http://download.csdn.net/detail/haishu_zheng/9555916

Github: https://github.com/zhenghaishu/Spring-Security-Demo

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Spring Security是一个用于身份验证和授权的框架,在Spring项目中提供了一套强大的安全性解决方案。以下是你入门Spring Security的步骤: 1. 添加Spring Security依赖:在你的项目中,通过Maven或Gradle添加Spring Security的依赖。例如,在Maven中,你可以添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 2. 配置Spring Security:创建一个配置类来配置Spring Security。这个配置类需要继承`WebSecurityConfigurerAdapter`类,并覆盖`configure`方法。例如,你可以创建一个类叫做`SecurityConfig`: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/public/**").permitAll() // 允许公共访问的URL .anyRequest().authenticated() // 其他URL需要身份验证 .and() .formLogin() // 启用表单登录 .loginPage("/login") // 自定义登录页面URL .permitAll() .and() .logout() // 启用注销 .permitAll(); } } ``` 上述配置中,我们定义了哪些URL是公开访问的,哪些URL需要身份验证,以及自定义了登录和注销的相关配置。 3. 创建用户服务:在上面的配置类中,你需要定义一个用户服务来获取用户的身份验证信息。这可以通过实现`UserDetailsService`接口来完成。你可以创建一个类叫做`UserService`来实现这个接口,并重写`loadUserByUsername`方法: ```java @Service public class UserService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 从数据库或其他数据源中获取用户信息 // 然后返回一个实现了UserDetails接口的类,代表用户的身份验证信息 // 例如,你可以使用Spring Security提供的User类 return User.builder() .username(username) .password("password") .roles("USER") .build(); } } ``` 上述代码中,我们简单地返回了一个固定的用户信息,实际应用中你需要从数据库或其他数据源中获取真实的用户信息。 4. 配置密码编码器:为了安全起见,你需要对用户密码进行编码。在上述的配置类中,通过重写`configure`方法来配置密码编码器。例如: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); } // 其他配置... } ``` 上述代码中,我们使用了`BCryptPasswordEncoder`来对密码进行编码。 这些是入门Spring Security的基本步骤。当你完成了上述配置后,你的应用程序将需要进行身份验证,并且可以通过URL保护来限制访问。你可以根据需要进一步自定义和扩展Spring Security的功能。希望这能帮助到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值