spring security入门

2022/1/2 23:08:11

本文主要是介绍spring security入门,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

文章目录

  • 1 入门案例
    • 1.1 引入依赖
    • 1.2 web配置
    • 1.3 security配置
    • 1.4 查询用户
    • 1.5 登录页面
  • 2 过滤器
  • 3 页面动态展示
  • 4 权限控制
    • 4.1 开启注解支持
    • 4.2 添加注解
    • 4.3 友好页面展示
      • 4.3.1 security配置文件设置
      • 4.3.2 web配置文件设置
      • 4.3.3 异常处理器

Spring Security基于servlet过滤器实现的权限管理框架,是AOP思想的具体体现,提供了完善的认证机制和方法级的授权功能。本文基于springmvc项目。

1 入门案例

查询数据库用户信息,实现登录认证。

1.1 引入依赖

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

1.2 web配置

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
     <servlet>
         <servlet-name>dispatcherServlet</servlet-name>
         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
         <init-param>
             <param-name>contextConfigLocation</param-name>
             <!--spring容器,是父容器,spring-mvc和spring-security为子容器,导入到父容器中-->
             <param-value>classpath:applicationContext.xml</param-value>
         </init-param>
         <load-on-startup>1</load-on-startup>
     </servlet>
     <servlet-mapping>
         <servlet-name>dispatcherServlet</servlet-name>
         <url-pattern>/</url-pattern>
     </servlet-mapping>
     <!--编码过滤器,解决乱码问题-->
     <filter>
         <filter-name>characterEncodingFilter</filter-name>
         <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
         <init-param>
             <param-name>encoding</param-name>
             <param-value>UTF-8</param-value>
         </init-param>
     </filter>
     <filter-mapping>
         <filter-name>characterEncodingFilter</filter-name>
         <url-pattern>/*</url-pattern>
     </filter-mapping>
    <!--过滤器链,对象名称不能改成其他-->
    <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>
</web-app>

1.3 security配置

创建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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security.xsd">
    <!--设置不经过SpringSecurity过滤器的静态资源-->
    <security:http pattern="/css/**" security="none"/>
    <security:http pattern="/img/**" security="none"/>
    <security:http pattern="/plugins/**" security="none"/>
    <security:http pattern="/errors/**" security="none"/>

    <!--设置可以用el表达式配置,并自动生成对应过滤器-->
    <security:http auto-config="true" use-expressions="true">
        <!--指定可以被匿名访问的页面-->
        <security:intercept-url pattern="/login.jsp" access="permitAll()"/>
        <!--使用el表达式来指定项目所有资源访问都必须有ROLE_USER或ROLE_ADMIN角色,才能访问-->
        <security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')"/>
        <!--指定自定义的登录页面-->
        <security:form-login login-page="/login.jsp"
                             login-processing-url="/login" default-target-url="/index.jsp"
                             authentication-failure-url="/errors/failer.jsp"/>
        <!--指定退出后跳转到哪个页面-->
        <security:logout logout-url="/logout"
                         logout-success-url="/login.jsp"/>
        <!--登录页面中记住我功能,开启remember-me过滤器,设置token存储时间为60秒,此处存储在cookie中,也可设置存储在数据库中-->
        <security:remember-me token-validity-seconds="60"/>
    </security:http>
    <!--设置认证用户信息的来源-->
    <security:authentication-manager>
        <!--从数据库中查找用户名和密码,user-service-ref指定查询服务对象-->
        <security:authentication-provider user-service-ref="userService">
            <!--下面设置固定用户名和密码-->
            <!--<security:user-service>-->
                <!--{noop}指定password不加密-->
                <!--<security:user name="user" password="{noop}user" authorities="ROLE_USER"/>-->
            <!--</security:user-service>-->

            <!--指定加密对象-->
            <security:password-encoder ref="passwordEncoder"/>
            
        </security:authentication-provider>
    </security:authentication-manager>
</beans>

主配置文件applicationContext.xml中引入spring-security.xml,创建加密对象(用于密码加密保存和加密认证):

<import resource="classpath:spring-security.xml"/>
<!--加密对象-->
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

1.4 查询用户

userService实现:

@Service("userService")
public class UserServiceImp implements UserService {

    @Resource
    private UserDao userDao;
    @Resource
    private BCryptPasswordEncoder passwordEncoder;

    public List<User> selectAll() {
        return userDao.findAll();
    }

    public void addUser(User user) {
        user.setPassword(passwordEncoder.encode(user.getPassword()));//加密存储
        userDao.addUser(user);
    }

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        List<User> users = userDao.findByUserName(username);
        if(users == null || users.size() == 0){
            throw new UsernameNotFoundException(String.format("JdbcDaoImpl.notFound %s",username));
        }
        User user = users.get(0);
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        for(Role role : user.getRoles()){
            grantedAuthorities.add(new SimpleGrantedAuthority(role.getRoleName()));
        }
        //密码前拼接"{noop}",表示密码不加密验证,不加,需要加密验证
        //本地项目中的User类和spring security中的冲突了,直接带包名创建
        return new org.springframework.security.core.userdetails.User(user.getUserName(),user.getPassword(),user.getStatus()==1,true,true,true,grantedAuthorities) ;
    }

    public void updateUser(User user) {
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        userDao.updateUser(user);
    }
}

要事先准备好用户表、角色表、权限表、用户角色表、角色权限表,并添加一些数据。

--先删除关联表
DROP TABLE IF EXISTS `SYS_ROLE_PERMISSION`;
DROP TABLE IF EXISTS `SYS_USER_ROLE`;

DROP TABLE IF EXISTS `SYS_USER`;
CREATE TABLE `SYS_USER` (
  `ID` INT(11) NOT NULL AUTO_INCREMENT,
  `USER_NAME` VARCHAR(32) NOT NULL COMMENT '用户名称',
  `PASSWORD` VARCHAR(120) CHARACTER SET UTF8 COLLATE UTF8_GENERAL_CI NOT NULL COMMENT '密码',
  `STATUS` INT(1) DEFAULT '1' COMMENT '1有效,0无效',
  PRIMARY KEY (`ID`)
) ENGINE=INNODB DEFAULT CHARSET=UTF8;

DROP TABLE IF EXISTS `SYS_ROLE`;

CREATE TABLE `SYS_ROLE` (
  `ID` INT(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
  `ROLE_NAME` VARCHAR(30) DEFAULT NULL COMMENT '角色名称',
  `ROLE_DESC` VARCHAR(60) DEFAULT NULL COMMENT '角色描述',
  PRIMARY KEY (`ID`)
) ENGINE=INNODB DEFAULT CHARSET=UTF8;

CREATE TABLE `SYS_USER_ROLE` (
  `UID` INT(11) NOT NULL COMMENT '用户编号',
  `RID` INT(11) NOT NULL COMMENT '角色编号',
  PRIMARY KEY (`UID`,`RID`),
  CONSTRAINT `FK_REFERENCE_1` FOREIGN KEY (`RID`) REFERENCES `SYS_ROLE` (`ID`),
  CONSTRAINT `FK_REFERENCE_2` FOREIGN KEY (`UID`) REFERENCES `SYS_USER` (`ID`)
) ENGINE=INNODB DEFAULT CHARSET=UTF8;

DROP TABLE IF EXISTS `SYS_PERMISSION`;
CREATE TABLE `SYS_PERMISSION` (
  `ID` INT(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
  `PERMISSION_NAME` VARCHAR(30) DEFAULT NULL COMMENT '菜单名称',
  `PERMISSION_URL` VARCHAR(100) DEFAULT NULL COMMENT '菜单地址',
  `PARENT_ID` INT(11) NOT NULL DEFAULT '0' COMMENT '父菜单ID',
   PRIMARY KEY (`ID`)
) ENGINE=INNODB DEFAULT CHARSET=UTF8;

CREATE TABLE `SYS_ROLE_PERMISSION` (
  `RID` INT(11) NOT NULL COMMENT '角色编号',
  `PID` INT(11) NOT NULL COMMENT '权限编号',
  PRIMARY KEY (`RID`,`PID`),
  CONSTRAINT `FK_REFERENCE_3` FOREIGN KEY (`RID`) REFERENCES `SYS_ROLE` (`ID`),
  CONSTRAINT `FK_REFERENCE_4` FOREIGN KEY (`PID`) REFERENCES `SYS_PERMISSION` (`ID`)
) ENGINE=INNODB DEFAULT CHARSET=UTF8;

1.5 登录页面

login.jsp页面中添加标签库,表单标签下添加标签<security:csrfInput/>

<%@taglib prefix="security" uri="http://www.springframework.org/security/tags"%>
<form method="post"> 
	<security:csrfInput/>
	<!--记住我,name属性值必须为remember-me-->
	<input type="checkbox" name="remember-me" value="true">
</form>

还需要写个login之后的跳转Controller,最后启动tomcat,进行登录验证。

“记住我”功能补充说明
将用户名密码和token都存在客户端相对不安全,另一种方法是在cookie中仅保存一个无意义的加密串,在数据库中保存加密串与登录用户(用户名、密码、token、时间)的对应关系,下次自动登录时,cookie中的加密串与数据库中的数据一同进行验证。
添加存储表PERSISTENT_LOGINS:

CREATE TABLE `PERSISTENT_LOGINS` ( 
     `SERIES` VARCHAR(64) NOT NULL, 
     `USERNAME` VARCHAR(64) NOT NULL, 
     `TOKEN` VARCHAR(64) NOT NULL, 
     `LAST_USED` TIMESTAMP NOT NULL, 
      PRIMARY KEY (`SERIES`)
) ENGINE=INNODB DEFAULT CHARSET=UTF8;

spring-security.xml中:

<security:remember-me data-source-ref="dataSource" token-validity-seconds="60" />

2 过滤器

spring security的核心是过滤器,一系列过滤器组成了过滤器链,如下图:
在这里插入图片描述

序号过滤器说明
1SecurityContextPersistenceFilter使用SecurityContextRepository在session中保存或更新一个 SecurityContext,并将SecurityContext给过滤器链中之后的过滤器使用。 SecurityContext中存储了当前用户的认证以及权限信息。
2WebAsyncManagerIntegrationFilter用于集成SecurityContext到Spring异步执行机制中的WebAsyncManager。
3HeaderWriterFilter向请求的Header中添加信息,可在<security:http>标签内部使用<security:headers>来控制
4CsrfFilter跨域请求伪造过滤,对所有post请求验证是否包含系统生成的csrf的token信息, 如果不包含,则报错。可防止csrf攻击。<security:csrf>可设置禁用,如果为开启,则必须为post请求。
5LogoutFilter匹配URL为/logout的请求,实现用户退出,清除认证信息。
6UsernamePasswordAuthenticationFilter主要负责认证的过滤器,默认匹配URL为/login且必须为POST请求。
7DefaultLoginPageGeneratingFilter如果没有在配置文件中指定认证页面,则由该过滤器生成一个默认认证页面。
8DefaultLogoutPageGeneratingFilter生成一个默认的退出登录页面
9BasicAuthenticationFilter自动解析HTTP请求中头部名字为Authentication,且以Basic开头的头信息。
10RequestCacheAwareFilter通过HttpSessionRequestCache内部维护了一个RequestCache,用于缓存HttpServletRequest
11SecurityContextHolderAwareRequestFilter针对ServletRequest进行了一次包装,使得request具有更加丰富的API
12AnonymousAuthenticationFilter当SecurityContextHolder中认证信息为空,则会创建一个匿名用户存入到SecurityContextHolder中。为了兼容未登录的访问,也走了一套认证流程,只不过是一个匿名的身份。
13SessionManagementFilterSecurityContextRepository限制同一用户开启多个会话的数量
14ExceptionTranslationFilter异常转换过滤器,用来转换整个链中出现的异常
15FilterSecurityInterceptor获取所配置资源访问的授权信息,根据SecurityContextHolder中存储的用户信息来判断其是否有权限。

3 页面动态展示

标签说明
<security:authorize access=“hasAnyRole(‘ROLE_ADMIN’)”>页面中某些内容需要根据角色显示时,用<security:authorize>包裹
<security:authentication property=“principal.username”/>显示用户名,property属性值也可以为name

4 权限控制

通过注解的方式来控制类或者方法的访问权限。

4.1 开启注解支持

application.xml或者spring-mvc.xml中添加配置:

<!--开启security,jsr250,spring注解支持-->
<security:global-method-security secured-annotations="enabled" jsr250-annotations="enabled" pre-post-annotations="enabled"/>

4.2 添加注解

Controller或者Service类或者方法上添加注解:

注解说明
@Secured({“ROLE_ADMIN”,“ROLE_USER”})security的注解,有ROLE_ADMIN或ROLE_USER角色才能访问
@RolesAllowed({“ROLE_ADMIN”,“ROLE_USER”})jsr250的注解
@PreAuthorize(“hasAnyRole(‘ROLE_ADMIN’,‘ROLE_USER’)”)spring的注解

4.3 友好页面展示

禁止访问时,友好页面展示的方式:

4.3.1 security配置文件设置

spring-security.xml中<security:http>下添加:

<security:access-denied-handler error-page="/errors/403.jsp"/>

4.3.2 web配置文件设置

web.xml中添加:

<error-page>
    <error-code>403</error-code>
    <location>/errors/403.jsp</location>
</error-page>

4.3.3 异常处理器

@ControllerAdvice
public class ControllerException {

    @ExceptionHandler(AccessDeniedException.class)
    public String exception403Advice(){
        return"redirect:/errors/403.jsp";
    }
}


这篇关于spring security入门的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程