Spring Security


概述

Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架。
(https://projects.spring.io/spring-security/) Spring Security 为基于J2EE企业应用软件提供了全面安全服务

安全包括两个主要操作。
“认证”,是为用户建立一个他所声明的主体。主题一般式指用户,设备或可以在你系 统中执行动作的其他系
统。
“授权”指的是一个用户能否在你的应用中执行某个操作,在到达授权判断之前,身份的主题已经由 身份验证
过程建立了。

使用

Maven依赖

<dependencies>
  <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>5.0.1.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>5.0.1.RELEASE</version>
  </dependency>
</dependencies>

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"
         version="3.1">

    <display-name>Archetype Created Web Application</display-name>
    <!-- 配置加载类路径的配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml,classpath*:spring-security.xml</param-value>
    </context-param>

    <!-- 配置监听器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- 配置监听器,监听request域对象的创建和销毁的 -->
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</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>

    <!-- 前端控制器(加载classpath:springmvc.xml 服务器启动创建servlet) -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置初始化参数,创建完DispatcherServlet对象,加载springmvc.xml配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- 服务器启动的时候,让DispatcherServlet对象创建 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>login.html</welcome-file>
        <welcome-file>login.htm</welcome-file>
        <welcome-file>login.jsp</welcome-file>
<!--        <welcome-file>index.html</welcome-file>-->
<!--        <welcome-file>index.htm</welcome-file>-->
<!--        <welcome-file>index.jsp</welcome-file>-->
<!--        <welcome-file>default.html</welcome-file>-->
<!--        <welcome-file>default.htm</welcome-file>-->
<!--        <welcome-file>default.jsp</welcome-file>-->
    </welcome-file-list>
</web-app>

spring security配置

<?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="/login.jsp"/>
    <security:http security="none" pattern="/failure.jsp"/>
    <security:http pattern="/css/**" security="none"/>
    <security:http pattern="/img/**" security="none"/>
    <security:http pattern="/plugins/**" security="none"/>
    <security:http pattern="/js/**" security="none"/>
    <security:http pattern="/WEB-INF/**" security="none"/>


    <security:http auto-config="true" use-expressions="false">

        <!-- 配置资源权限,表示任意资源路径都需要ROLE_USER权限 -->
        <security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN"/>

        <!-- 自定义登陆页面,
            login-page 自定义登陆页面
            authentication-failure-url 用户权限校验失败之后才会跳转到这个页面,如果数据库中没有这个用户则不会跳转到这个页面。
            default-target-url 登陆成功后跳转的页面。
            注:登陆页面用户名固定 username,密码password,action:login
         -->
        <security:form-login
                login-page="/login.jsp"
                login-processing-url="/login" username-parameter="username"
                password-parameter="password" authentication-failure-url="/failure.jsp"
                default-target-url="/pages/main.jsp"
        ></security:form-login>

        <!-- 注销,
            invalidate-session 是否删除session
            logout-url:注销处理链接
            logout-success-url:注销成功页面
            注:注销操作 只需要链接到 logout即可注销当前用户
        -->
        <security:logout invalidate-session="true" logout-url="/logout"
                         logout-success-url="/login.jsp"/>

        <!-- 关闭CSRF,默认是开启的 关闭跨域请求-->
        <security:csrf disabled="true"/>
    </security:http>
    <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"/>-->
</beans>

Spring Security使用数据库认证

使用UserDetails、UserDetailsService来完成操作
UserDetails是一个接口,作用是于封装当前进行认证的用户信息,
可以手动对其进行实现,也可以使用Spring Security提供的实现类User来完成操作
自定义的UserService继承UserDetailsService接口

public interface UsersService extends UserDetailsService {
}

实现类

@Service("userService")
@Transactional
public class UsersServiceImpl implements UsersService {
    @Autowired
    private UsersDao usersDao;

    //  该方法为UserDetailsService的方法,需实现该方法
    @Override
    public UserDetails loadUserByUsername(String username)  {
        System.out.println(username);
        Users user = usersDao.findByUsername(username);
        //  这里不对密码加密时需要拼接 {noop}
        //  这是security的规定
        User securityUser = new User(user.getUsername(), "{noop}" + user.getPassword(),
                user.getStatus() == 0 ? false : true, true, true, true, getAuthority(user.getRoles()));
        return securityUser;
    }

    //  该用户的角色列表
    private List<SimpleGrantedAuthority> getAuthority(List<Role> roles) {
        List<SimpleGrantedAuthority> authorities = new ArrayList();
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
        }
        return authorities;
    }
}

用户密码加密处理

spring-security.xml

<security:authentication-manager>
    <security:authentication-provider user-service-ref="usersService">
        <!-- 配置加密的方式 使用该方式必须对用户密码进行加密处理 -->
        <security:password-encoder ref="passwordEncoder"/>

    </security:authentication-provider>
</security:authentication-manager>
<!-- 配置加密类 -->
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

UserServiceImpl.java


@Service("usersService")
@Transactional
public class UsersServiceImpl implements UsersService {
    @Autowired
    private UsersDao usersDao;
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;


    @Override
    public UserDetails loadUserByUsername(String username)  {
        System.out.println(username);
        Users user = usersDao.findByUsername(username);
        //  使用加密;这里不再需要拼接 {noop}
        User securityUser = new User(user.getUsername(),  user.getPassword(),
                user.getStatus() == 0 ? false : true, true, true, true, getAuthority(user.getRoles()));
        return securityUser;
    }

    @Override
    public List<Users> findAll(Integer page, Integer pageSize) {
        PageHelper.startPage(page, pageSize);
        return usersDao.findAll();
    }

    //  保存时必须使用xml中配置的加密方式
    @Override
    public int save(Users users) {
        //  生成ID
        users.setId(UUID.randomUUID().toString().replace("-","" ).substring(0,10));
        //  bCryptPasswordEncoder.encode 密码加密
        String encode = bCryptPasswordEncoder.encode(users.getPassword());
        users.setPassword(bCryptPasswordEncoder.encode(users.getPassword()));
        return usersDao.save(users);
    }
}

注解对方法级进行权限控制

Spring Security在方法的权限控制上
支持三种类型的注解,JSR-250注解、@Secured注解和支持表达式的注解,这三种注解默认都是没有启用的,需要
单独通过global-method-security元素的对应属性进行启用
注解开启
@EnableGlobalMethodSecurity :Spring Security默认是禁用注解的,要想开启注解,需要在继承
WebSecurityConfigurerAdapter的类上加@EnableGlobalMethodSecurity注解,并在该类中将
AuthenticationManager定义为Bean。
spring-security.xml配置文件

<security:global-method-security jsr250-annotations="enabled"/> 
<security:global-method-security secured-annotations="enabled"/> 
<security:global-method-security pre-post-annotations="disabled"/>
<!-- 使用注解需要开启表达式  -->
<!-- use-expressions="true"开启使用表达式  -->
    <security:http auto-config="true" use-expressions="true">
        <!-- 配置具体的拦截的规则 pattern="请求路径的规则" access="访问系统的人,必须有ROLE_USER的角色" -->
        <security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')"/>

        <!-- 定义跳转的具体的页面 -->
        <security:form-login
                login-page="/login.jsp"
                login-processing-url="/login.do"
                default-target-url="/pages/main.jsp"
                authentication-failure-url="/failer.jsp"
        />

        <!-- 关闭跨域请求 -->
        <security:csrf disabled="true"/>

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

    </security:http>

JSR-250注解

@RolesAllowed表示访问对应方法时所应该具有的角色
示例:
@RolesAllowed({"USER", "ADMIN"}) 该方法只要具有"USER", "ADMIN"任意一种权限就可以访问。这里可以省略前缀ROLE_,实际的权限可能是ROLE_ADMIN

@PermitAll表示允许所有的角色进行访问,也就是说不进行权限控制
@DenyAll是和PermitAll相反的,表示无论什么角色都不能访问

支持表达式的注解

@PreAuthorize 在方法调用之前,基于表达式的计算结果来限制对方法的访问
@PostAuthorize 允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常
@PostFilter 允许方法调用,但必须按照表达式来过滤方法的结果
@PreFilter 允许方法调用,但必须在进入方法之前过滤输入值

@Secured注解

@Secured注解标注的方法进行权限控制的支持,其值默认为disabled。
示例:


@Secured("IS_AUTHENTICATED_ANONYMOUSLY") 
public Account readAccount(Long id); 
@Secured("ROLE_TELLER")

页面端标签控制权限

在jsp页面中使用spring security提供的权限标签来进行权限控制

maven导入

<dependency> 
 <groupId>org.springframework.security</groupId> 
 <artifactId>spring-security-taglibs</artifactId> 
 <version>version</version> 
</dependency>

jsp导入

<%@taglib uri="http://www.springframework.org/security/tags" prefix="security"%>

常用标签

authentication
authentication代表的是当前认证对象,可以获取当前认证对象信息
<security:authentication property="" htmlEscape="" scope="" var=""/>
property: 只允许指定Authentication所拥有的属性,可以进行属性的级联获取,如“principle.username”,
不允许直接通过方法进行调用
htmlEscape:表示是否需要将html进行转义。默认为true。
scope:与var属性一起使用,用于指定存放获取的结果的属性名的作用范围,默认我pageContext。Jsp中拥
有的作用范围都进行进行指定
var: 用于指定一个属性名,这样当获取到了authentication的相关信息后会将其以var指定的属性名进行存
放,默认是存放在pageConext中

authorize
authorize是用来判断普通权限的,通过判断用户是否具有对应的权限而控制其所包含内容的显示
<security:authorize access="" method="" url="" var=""></security:authorize>
access: 需要使用表达式来判断权限,当表达式的返回结果为true时表示拥有对应的权限
method:method属性是配合url属性一起使用的,表示用户应当具有指定url指定method访问的权限,
method的默认值为GET,可选值为http请求的7种方法
url:url表示如果用户拥有访问指定url的权限即表示可以显示authorize标签包含的内容
var:用于指定将权限鉴定的结果存放在pageContext的哪个属性中

accesscontrollist
accesscontrollist标签是用于鉴定ACL权限的。其一共定义了三个属性:hasPermission、domainObject和var,
其中前两个是必须指定的
<security:accesscontrollist hasPermission="" domainObject="" var=""></security:accesscontrollist>
hasPermission:hasPermission属性用于指定以逗号分隔的权限列表
domainObject:domainObject用于指定对应的域对象
var:var则是用以将鉴定的结果以指定的属性名存入pageContext中,以供同一页面的其它地方使用


文章作者: zrh
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 zrh !
  目录