天天看点

Spring Security 快速入门

一、简介

Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架。

(官方文档:https://spring.io/projects/spring-security)

二、快速入门

pom.xml

<properties>
    <spring.security.version>5.0.1.RELEASE</spring.security.version>
</properties>

<dependencies>
	<dependency>
	    <groupId>org.springframework.security</groupId>
	    <artifactId>spring-security-web</artifactId>
	    <version>${spring.security.version}</version>
	</dependency>
	<dependency>
	    <groupId>org.springframework.security</groupId>
	    <artifactId>spring-security-config</artifactId>
	    <version>${spring.security.version}</version>
	</dependency>
</dependencies>
           

web.xml

<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.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 auto-config="true" use-expressions="false">
	<security:intercept-url pattern="/**" access="ROLE_USER" />
</security:http>

<security:authentication-manager>
	<security:authentication-provider>
		<security:user-service>
		<security:user name="user" password="{noop}user"
		authorities="ROLE_USER" />
		<security:user name="admin" password="{noop}admin"
		authorities="ROLE_ADMIN" />
		</security:user-service>
	</security:authentication-provider>
</security:authentication-manager>
</beans>
           

参数说明:

  • auto-config:使用默认配置(如Spring Security提供的登录页面)
  • use-expressions:Spring 表达式语言配置访问控制
  • intercept-url:定义一个过滤规则
    • pattern:表示对哪些url进行权限控制
    • access:表示在请求对应的URL时需要什么权限
  • authentication-manager:配置用户

三、基本操作

1、配置静态资源免过滤

2、自定义登录页面

<security:http security="none" pattern="/index.jsp"/>
<security:http security="none" pattern="/pswFail.jsp"/>
           
<security:form-login login-page="/"
login-processing-url="/login" username-parameter="username"
password-parameter="password" authentication-failure-url="/pswFail.jsp"
default-target-url="/admin"/>
           
  • login-page:登陆页面路径
  • login-processing-url:登录请求拦截的url,也就是form表单提交时指定的action
  • username-parameter:用户名的请求字段 默认为username
  • password-parameter:密码的请求字段 默认为password
  • authentication-failure-url:登录失败后跳转的url
  • default-target-url:默认登录成功后跳转的url
  • 其它参数
    • always-use-default-target:是否总是使用默认的登录成功后跳转url
    • authentication-success-handler-ref:指向一个AuthenticationSuccessHandler用于处理认证成功的请求,不能和default-target-url还有always-use-default-target同时使用
    • authentication-success-forward-url:用于authentication-failure-handler-ref
    • authentication-failure-handler-ref:指向一个AuthenticationFailureHandler用于处理失败的认证请求
    • authentication-failure-forward-url:用于authentication-failure-handler-ref
    • authentication-details-source-ref:指向一个AuthenticationDetailsSource,在认证过滤器中使用

3、自定义无权限跳转页面

<security:http auto-config="xxx" use-expressions="xxx">
	<security:access-denied-handler error-page="/authFail.jsp"/>
</security:http>
           

4、关闭CSRF

<security:http auto-config="xxx" use-expressions="xxx">
	<!-- 关闭CSRF,默认是开启的 -->
	<security:csrf disabled="true" />
</security:http>
           

5、用户退出

<!--退出-->
<security:logout invalidate-session="true" logout-url="/logout" logout-success-url="/"/>
           

6、综合

<?xml version="1.0" encoding="UTF-8"?>
<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 security="none" pattern="/index.jsp"/>
    <security:http security="none" pattern="/pswFail.jsp"/>
    <security:http security="none" pattern="/authFail.jsp"/>
    <security:http security="none" pattern="/static/**"/>

    <security:http auto-config="true" use-expressions="false">
        <!--auto-config="true" 指使用默认配置-->
        <!-- intercept-url定义一个过滤规则 pattern表示对哪些url进行权限控制,access属性表示在请求对应
        的URL时需要什么权限,
        默认配置时它应该是一个以逗号分隔的角色列表,请求的用户只需拥有其中的一个角色就能成功访问对应
        的URL -->
        <security:access-denied-handler error-page="/authFail.jsp"/>

        <!--pattern="/**" 指所有资源的验证权限
            access="ROLE_USER" 指能访问的角色-->
        <security:intercept-url pattern="/**" access="ROLE_USER" />

        <!--
            1. login-page 自定义登录页url,默认为/login
            2. login-processing-url 登录请求拦截的url,也就是form表单提交时指定的action
            3. default-target-url 默认登录成功后跳转的url
            4. always-use-default-target 是否总是使用默认的登录成功后跳转url
            5. authentication-failure-url 登录失败后跳转的url
            6. username-parameter 用户名的请求字段 默认为userName
            7. password-parameter 密码的请求字段 默认为password
            8. authentication-success-handler-ref 指向一个AuthenticationSuccessHandler用于处理认证成功的请求,不能和default-target-url还有always-use-default-target同时使用
            9. authentication-success-forward-url 用于authentication-failure-handler-ref
            10. authentication-failure-handler-ref 指向一个AuthenticationFailureHandler用于处理失败的认证请求
            11. authentication-failure-forward-url 用于authentication-failure-handler-ref
            12. authentication-details-source-ref 指向一个AuthenticationDetailsSource,在认证过滤器中使用
         -->
        <security:form-login login-page="/"
                             login-processing-url="/login" username-parameter="username"
                             password-parameter="password" authentication-failure-url="/pswFail.jsp"
                             default-target-url="/admin"
        />
        
		<!--退出-->
		<security:logout invalidate-session="true" logout-url="/logout" logout-success-url="/"/>

        <!-- 关闭CSRF,默认是开启的 -->
        <security:csrf disabled="true" />

    </security:http>

    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="user" password="{noop}user"
                               authorities="ROLE_USER" />
                <security:user name="admin" password="{noop}admin"
                               authorities="ROLE_ADMIN" />
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>
           

测试

登录admin账号

Spring Security 快速入门

密码错误

Spring Security 快速入门

登录成功(此时admin账号无权限)

Spring Security 快速入门

登录user(此时user账号有权限)

Spring Security 快速入门

7、问题解决

(1)登录成功后跳转到favicon.ico

  • 原因

    SpringSecurity默认是先去获取浏览器标签页网站图标的,由于没有加权限过滤就会导致该问题。

  • 解决方法(参考:https://www.cnblogs.com/qiannianguyao/p/11737004.html)

    给登录页面添加图标。

四、使用数据库认证

1、基础配置

spring-security.xml

<security:authentication-manager>
    <security:authentication-provider user-service-ref="userService">
    </security:authentication-provider>
</security:authentication-manager>
           

IUserService.java(需要继承框架提供的UserDetailsService)

package com.ssm.service;

import org.springframework.security.core.userdetails.UserDetailsService;

public interface IUserService extends UserDetailsService {
}

           

UserServiceImpl.java

package com.ssm.service.impl;

import com.ssm.dao.UsersDao;
import com.ssm.domain.UserInfo;
import com.ssm.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
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.UsernameNotFoundException;
import org.springframework.stereotype.Service;

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

@Service("userService")
public class UserServiceImpl implements IUserService {

    @Autowired
    UsersDao usersDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserInfo userInfo = null;
        User user = null;

        try {
            userInfo = usersDao.findUser(username);
            //将自己的User对象封装成框架的User对象
            user = new User(userInfo.getUsername(),"{noop}" + userInfo.getPsw(),getAuthority());
        } catch (Exception e) {
            e.printStackTrace();
        }

        return user;
    }

	//设置权限
    private List<SimpleGrantedAuthority> getAuthority() {
        List<SimpleGrantedAuthority> list = new ArrayList<>();
        list.add(new SimpleGrantedAuthority("ROLE_USER"));
        return list;
    }
}

           

注意:

传入密码参数需要加前缀:{noop}

否则报错:

2、使用自定义用户类型

id roleName roleDesc
1 ADMIN 管理员
2 USER 普通用户

配置允许访问的用户名(必须以ROLE_开头)

UserServiceImpl .java

package com.ssm.service.impl;

import com.ssm.dao.UsersDao;
import com.ssm.domain.Role;
import com.ssm.domain.UserInfo;
import com.ssm.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
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.UsernameNotFoundException;
import org.springframework.stereotype.Service;

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

@Service("userService")
public class UserServiceImpl implements IUserService {

    @Autowired
    UsersDao usersDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserInfo userInfo = null;
        User user = null;

        try {
            userInfo = usersDao.findUser(username);
            //将自己的User对象封装成框架的User对象
            user = new User(userInfo.getUsername(),"{noop}" + userInfo.getPsw(),getAuthority(userInfo.getRoles()));
        } catch (Exception e) {
            e.printStackTrace();
        }

        return user;
    }

    //设置权限
    private List<SimpleGrantedAuthority> getAuthority(List<Role> roles) {
        List<SimpleGrantedAuthority> list = new ArrayList<>();

        roles.forEach(role->list.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName())));
        return list;
    }
}

           

3、根据用户状态设置访问

如果用户的状态为关闭,则不能让其登录

UserServiceImpl .java

user = new User(userInfo.getUsername(),"{noop}" + userInfo.getPsw(),
                    userInfo.getUserStatus() == 1,
                    true,true,true,
                    getAuthority(userInfo.getRoles()));
           

4、使用加密方式登录

<security:authentication-manager>
        <security:authentication-provider user-service-ref="userService">
            <!--引入加密-->
            <security:password-encoder ref="passwordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>

    <!--加密-->
    <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
           

UserServiceImpl.java

将之前 userInfo.getPsw() 前面拼接的字符串“{noop}”去除

user = new User(userInfo.getUsername(),userInfo.getPsw(),
                    userInfo.getUserStatus() == 1,
                    true,true,true,
                    getAuthority(userInfo.getRoles()));
           

将生成的密码更新到数据库

import org.junit.Test;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class test {

    @Test
    public void test1() {
        String encode = new BCryptPasswordEncoder().encode("123456789");
        System.out.println(encode);
    }
}

           

五、服务器端方法级权限控制

Spring Security框架可以通过JSR-250注解、@Secured注解 和 支持表达式的注解 控制方法的权限,这三种注解默认都是没有启用的,需要单独通过global-method-security元素的对应属性进行启用。

1、JSR-250

<dependency>
	<groupId>javax.annotation</groupId>
	<artifactId>jsr250-api</artifactId>
	<version>1.0</version>
</dependency>
           

约束

xmlns:aop="http://www.springframework.org/schema/aop"
           
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
           

防止 JSR-250不生效

Controller(这里的USER 可以 省略ROLE_)

2、@Secured注解

开启支持

Controller(这里的USER 不可以 省略ROLE_)

3、JSR-250 与 @Secured注解 区别

  • JSR-250 需要导入新的依赖,而 @Secured注解 是Spring Security自带的。
  • JSR-250 可以省略ROLE_,而 @Secured 不能。

4、表达式注解

开启支持

Controller