简单的Spring Security实例(自定义登录验证)

来源:互联网 发布:思科网络课程 编辑:程序博客网 时间:2024/04/29 20:33

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>

运行测试

运行项目,项目启动后默认打开地址:http://localhost:8080/user/loginPage
这里写图片描述
输入错误用户名:hehe和密码:haha,此时登录失败,跳转到错误提示页面(http://localhost:8080/user/loginPage?error=error)
这里写图片描述
点击”点击这里”重新跳转到登录页面,输入正确用户名:xyc,密码:123,此时验证通过,跳转到主页(http://localhost:8080/)
这里写图片描述
点击”退出系统”,返回登录页面


以上就是简单的Spring Security实例。。。

0 0
原创粉丝点击