簡單的Spring Security例項(自定義登入驗證)
阿新 • • 發佈:2018-12-31
Spring Security是一個能夠為基於Spring的企業應用系統提供宣告式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉Inversion of Control ,DI:Dependency Injection 依賴注入)和AOP(面向切面程式設計)功能,為應用系統提供宣告式的安全訪問控制功能,減少了為企業系統安全控制編寫大量重複程式碼的工作。
目錄結構
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.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xyc</groupId>
<artifactId>spring.security</artifactId>
<packaging>war</packaging >
<version>1.0-SNAPSHOT</version>
<name>spring.security Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId> org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.1.2.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.1.2.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>spring.security</finalName>
</build>
</project>
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: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.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 設定使用註解的類所在的jar包 -->
<context:component-scan base-package="com.xyc.spring.security"></context:component-scan>
<!-- 啟用spring mvc 註解 -->
<mvc:annotation-driven/>
<!-- 對轉向頁面的路徑解析。prefix:字首, suffix:字尾 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
spring-security.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
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.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!--需要過濾不被攔截的請求-->
<security:http pattern="/user/loginPage" security="none" />
<security:http auto-config="true" use-expressions="true">
<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
<security:form-login login-page="/user/loginPage" authentication-failure-url="/user/loginPage?error=error" default-target-url="/user/index" login-processing-url="/login"/>
<security:logout invalidate-session="true" logout-success-url="/user/loginPage" logout-url="/logout"/>
<security:csrf disabled="true"/>
</security:http>
<bean id="loginUserDetailService" class="com.xyc.spring.security.config.impl.LoginUserDetailsServiceImpl"></bean>
<bean id="loginAuthenticationProvider" class="com.xyc.spring.security.config.LoginAuthenticationProvider">
<property name="userDetailsService" ref="loginUserDetailService"></property>
</bean>
<security:authentication-manager alias="myAuthenticationManager">
<security:authentication-provider ref="loginAuthenticationProvider">
</security:authentication-provider>
</security:authentication-manager>
</beans>
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<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_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>Spring Security</display-name>
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
<!-- Spring MVC配置 -->
<!-- ====================================== -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 可以自定義servlet.xml配置檔案的位置和名稱,預設為WEB-INF目錄下,名稱為[<servlet-name>]-servlet.xml,如spring-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- Spring Security begin -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-security.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<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>
<!-- Spring Security end -->
</web-app>
LoginUser
package com.xyc.spring.security.entity;
/**
* Created by xyc on 2016/8/22 0022.
*/
public class LoginUser {
private Long id;
private String username;
private String password;
public LoginUser() {
}
public LoginUser(Long id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public Long getId() {
return id;
}
public void setId(Long 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;
}
}
LoginAuthenticationProvider
package com.xyc.spring.security.config;
/**
* Created by xyc on 2016/8/22 0022.
*/
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.authentication.dao.SaltSource;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.Assert;
/**
* 重寫DaoAuthenticationProvider的驗證方法
*/
public class LoginAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
private SaltSource saltSource;
private LoginUserDetailsService userDetailsService;
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
Object salt = null;
if (this.saltSource != null) {
salt = this.saltSource.getSalt(userDetails);
}
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException("Bad credentials:" + userDetails);
}
String presentedPassword = authentication.getCredentials().toString();
if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt)) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException("Bad credentials:" + userDetails);
}
}
protected void doAfterPropertiesSet() throws Exception {
Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
}
protected PasswordEncoder getPasswordEncoder() {
return passwordEncoder;
}
protected SaltSource getSaltSource() {
return saltSource;
}
protected LoginUserDetailsService getUserDetailsService() {
return userDetailsService;
}
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
UserDetails loadedUser;
try {
String password = (String) authentication.getCredentials();
/**
* 區別:這裡使用的是自定義的驗證方法
*/
loadedUser = getUserDetailsService().loadUserByUsername(username, password);
} catch (UsernameNotFoundException notFound) {
throw notFound;
} catch (Exception repositoryProblem) {
throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
}
if (loadedUser == null) {
throw new AuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
/**
* Sets the PasswordEncoder instance to be used to encode and validate
* passwords. If not set, the password will be compared as plain text.
* <p>
* For systems which are already using salted password which are encoded
* with a previous release, the encoder should be of type
* {@code org.springframework.security.authentication.encoding.PasswordEncoder}
* . Otherwise, the recommended approach is to use
* {@code org.springframework.security.crypto.password.PasswordEncoder}.
*
* @param passwordEncoder must be an instance of one of the {@code PasswordEncoder}
* types.
*/
public void setPasswordEncoder(Object passwordEncoder) {
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
if (passwordEncoder instanceof PasswordEncoder) {
this.passwordEncoder = (PasswordEncoder) passwordEncoder;
return;
}
if (passwordEncoder instanceof org.springframework.security.crypto.password.PasswordEncoder) {
final org.springframework.security.crypto.password.PasswordEncoder delegate = (org.springframework.security.crypto.password.PasswordEncoder) passwordEncoder;
this.passwordEncoder = new PasswordEncoder() {
private void checkSalt(Object salt) {
Assert.isNull(salt, "Salt value must be null when used with crypto module PasswordEncoder");
}
public String encodePassword(String rawPass, Object salt) {
checkSalt(salt);
return delegate.encode(rawPass);
}
public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
checkSalt(salt);
return delegate.matches(rawPass, encPass);
}
};
return;
}
throw new IllegalArgumentException("passwordEncoder must be a PasswordEncoder instance");
}
/**
* The source of salts to use when decoding passwords. <code>null</code> is
* a valid value, meaning the <code>DaoAuthenticationProvider</code> will
* present <code>null</code> to the relevant <code>PasswordEncoder</code>.
* <p>
* Instead, it is recommended that you use an encoder which uses a random
* salt and combines it with the password field. This is the default
* approach taken in the
* {@code org.springframework.security.crypto.password} package.
*
* @param saltSource to use when attempting to decode passwords via the
* <code>PasswordEncoder</code>
*/
public void setSaltSource(SaltSource saltSource) {
this.saltSource = saltSource;
}
public void setUserDetailsService(LoginUserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
}
LoginUserDetailsImpl
package com.xyc.spring.security.config;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
/**
* Created by xyc on 2016/8/22 0022.
*/
public class LoginUserDetailsImpl extends User implements UserDetails {
private static final long serialVersionUID = -5424897749887458053L;
public LoginUserDetailsImpl(String username, String password, boolean enabled, boolean accountNonExpired,
boolean credentialsNonExpired, boolean accountNonLocked,
Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
}
public LoginUserDetailsImpl(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
public LoginUserDetailsImpl(String username, String password) {
super(username, password, new ArrayList<GrantedAuthority>());
}
}
LoginUserDetailsService
package com.xyc.spring.security.config;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* Created by xyc on 2016/8/22 0022.
*/
public interface LoginUserDetailsService {
/**
* 根據使用者名稱密碼驗證使用者資訊
* @param username 使用者名稱
* @param password 密碼
* @return
* @throws UsernameNotFoundException
*/
UserDetails loadUserByUsername(String username, String password) throws UsernameNotFoundException;
}
LoginUserDetailsServiceImpl
package com.xyc.spring.security.config.impl;
import com.xyc.spring.security.config.LoginUserDetailsImpl;
import com.xyc.spring.security.config.LoginUserDetailsService;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.ArrayList;
import java.util.Collection;
/**
* Created by xyc on 2016/8/22 0022.
*/
public class LoginUserDetailsServiceImpl implements LoginUserDetailsService {
/**
* 進行登入驗證
*/
public UserDetails loadUserByUsername(String username, String password) throws UsernameNotFoundException {
boolean result = this.validate(username, password);
if (!result) {
return null;
}
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
LoginUserDetailsImpl user = new LoginUserDetailsImpl(username, password,authorities);
return user;
}
/**
* 在此處驗證
* @param username
* @param password
* @return
*/
private boolean validate(String username, String password) {
/**
* 此處應該在資料庫獲取使用者資訊進行驗證
*/
if ("xyc".equals(username) && "123".equals(password)) {
return true;
}
return false;
}
}
UserController
package com.xyc.spring.security.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Created by xyc on 2016/8/20 0020.
*/
@Controller
@RequestMapping(value = {"/","/user"})
public class UserController {
@RequestMapping(value = {"/","/index"},method = RequestMethod.GET)
public String index(){
System.out.println("主頁");
return "index";
}
@RequestMapping(value = "/loginPage",method = RequestMethod.GET)
public String loginPage(@RequestParam(value = "error", required = false) String error, Model model){
System.out.println("登入頁");
if (error != null) {
return "loginFailure";
}
return "login";
}
}
index.jsp
<%--
Created by IntelliJ IDEA.
User: xyc
Date: 2016/8/21 0021
Time: 10:31
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>主頁</title>
</head>
<body style="text-align: center;margin-top: 300px;">
<h4>**您好!您已成功登陸本系統!<a href="/logout">退出系統</a></h4>
</body>
</html>
login.jsp
<%--
Created by IntelliJ IDEA.
User: xyc
Date: 2016/8/21 0021
Time: 10:31
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登入</title>
</head>
<body style="text-align: center;margin-top: 300px;">
<form method="post" action="/login">
<h4>登入模組</h4>
<div>
<input type="text" name="username" placeholder="使用者名稱">
<input type="password" name="password" placeholder="密碼">
<input type="submit" value="登入" name="submit">
</div>
</form>
</body>
</html>
loginFailure.jsp
<%--
Created by IntelliJ IDEA.
User: xyc
Date: 2016/8/22 0022
Time: 14:27
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登入失敗</title>
</head>
<body style="text-align: center;margin-top: 300px;">
<h4>登入失敗!請<a href="/user/loginPage">點選這裡</a>重新登陸!</h4>
</body>
</html>
執行測試
以上就是簡單的Spring Security例項。。。