美文网首页js css html
Spring Security入门

Spring Security入门

作者: 我可能是个假开发 | 来源:发表于2023-03-07 21:02 被阅读0次

一、入门案例

1.导入依赖:

<?xml version="1.0" encoding="UTF-8"?>

<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.hcx</groupId>
    <artifactId>springsecuritydemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>springsecuritydemo Maven Webapp</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>5.0.2.RELEASE</spring.version>
        <spring.security.version>5.0.1.RELEASE</spring.security.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <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>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
        <finalName>springsecuritydemo</finalName>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.2.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<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"
         version="2.5">
    <display-name>springsecuritydemo</display-name>

    <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-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!--springSecurityFilterChain不能变,只能是这个名字-->
    <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>

    <welcome-file-list>
        <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:

<?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">
        <!--
            配置资源路径,表示任意路径都需要ROLE_USER权限
            intercept-url:过滤规则,
            pattern:对哪些url进行权限控制,
            access:表示在请求对应的url时需要什么权限。
            默认配置时它应该是一个以逗号分隔的角色列表,请求的用户只需拥有其中的一个角色就能成功访问对应的URL
         -->
        <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>

访问 http://localhost:8090/login.html(端口根据自己配置的来,login.html是自带的登录页)

image.png

根据配置,配置了两类用户 user和admin:
使用user登陆,默认访问index.jsp页面


image.png

使用admin登陆:

image.png
配置了需要ROLE_USER的身份才可以登陆,admin用户配置的身份为ROLE_ADMIN,所以没有权限。

二、使用自定义页面

在webapp下定义三个页面:


image.png

login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
<form action="login" method="post">
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username"/></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="password"/></td>
        </tr>
        <tr>
            <td colspan="2" align="center"><input type="submit" value="登录"/>
                <input type="reset" value="重置"/></td>
        </tr>
    </table>
</form>
</body>
</html>

修改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 security="none" pattern="/login.html"/>
    <security:http security="none" pattern="/fail.html"/>

    <security:http auto-config="true" use-expressions="false">
        <!--
            配置资源路径,表示任意路径都需要ROLE_USER权限
            intercept-url:过滤规则,
            pattern:对哪些url进行权限控制,
            access:表示在请求对应的url时需要什么权限。
            默认配置时它应该是一个以逗号分隔的角色列表,请求的用户只需拥有其中的一个角色就能成功访问对应的URL
         -->
        <security:intercept-url pattern="/**" access="ROLE_USER"/>
        <!-- 自定义登陆页面,login-page 自定义登陆页面 authentication-failure-url 用户权限校验失败之后才会跳转到这个页面,如果数据库中没有这个用户则不会跳转到这个页面。
            default-target-url 登陆成功后跳转的页面。 注:登陆页面用户名固定 username,密码 password,action:login -->
        <security:form-login login-page="/login.html"
                             login-processing-url="/login" username-parameter="username"
                             password-parameter="password" authentication-failure-url="/fail.html"
                             default-target-url="/success.html" authentication-success-forward-url="/success.html"/>
        <!-- 关闭CSRF,默认是开启的 跨服务器的request forward访问-->
        <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>

三、使用数据库认证

三种使用方式:

①自己写实现类实现UserDetails接口(UserDetails封装用户认证信息的接口)

image.png

UserDetails用于封装当前正在进行认证的用户信息

②自己写实现类实现UserDetailsService接口:

image.png

用于规范在做认证时的具体方法

③使用Spring Security中UserDetails的实现类User

image.png

注意:使用了springSecurity之后,控制器由springSecurity完成

Demo:
UserService:

public interface UserService extends UserDetailsService{
    UserInfo findById(Integer id);
    void save(UserInfo userInfo);
}

UserServiceImpl:

@Service("userService")
@Transactional
public class UserServiceImpl implements UserService{

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
        UserInfo userInfo = null;
        try {
            userInfo = userMapper.selectByUsername(username);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //把用户对象封装为UserDetails
        //未使用密文时需要添加前缀
        //User user = new User(userInfo.getUsername(),"{noop}"+userInfo.getPassword(),getAuthorities(userInfo.getRoles()));
        User user = new User(userInfo.getUsername(),userInfo.getPassword(),
                userInfo.getStatus()==1?true:false,true,true,true,
                getAuthorities(userInfo.getRoles()));
//        User user = new User(userInfo.getUsername(),"{noop}"+userInfo.getPassword(), getAuthorities(userInfo.getRoles()));
        return user;
    }

    @Override
    public void save(UserInfo userInfo) {
        //对密码进行加密处理
        String encodePass = bCryptPasswordEncoder.encode(userInfo.getPassword());
        userInfo.setPassword(encodePass);
        userMapper.save(userInfo);
    }

对于使用密码加密的处理,可以直接注入BCryptPasswordEncoder类,使用bCryptPasswordEncoder.encode()方法,也可以使用工具类

密码加密工具类BCryptPasswordEncoderUtils:

package com.hcx.utils;

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

public class BCryptPasswordEncoderUtils {

    private static BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();

    public static String encodePassword(String password){
        return bCryptPasswordEncoder.encode(password);
    }

    public static void main(String[] args) {
        String s = encodePassword("123456");
        //每次加密的结果不同
        //$2a$10$04DqXfBW4UVVCuX2HZSn5e3Vm3DUfA7PUTqTMUBwVHQeEuIas5xia
        //$2a$10$RQ.j.T/d4yrnu4DU9chsYObTGrsnwCgRoEaewvOzvu/ov/z/D6aCi
        System.out.println(s);
    }
}

springSecurity.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 pattern="/login.jsp" security="none"/>
    <security:http pattern="/fail.jsp" security="none"/>
    <security:http pattern="/css/**" security="none"/>
    <security:http pattern="/img/**" security="none"/>
    <!--<security:http pattern="/js/**" security="none"/>-->
    <security:http pattern="/plugins/**" security="none"/>


    <!--
        配置具体的规则
        auto-config="true"  不用自己编写登录的页面,框架提供默认登录页面
        use-expressions="false" 是否使用SPEL表达式
    -->
    <!--<security:http auto-config="true" use-expressions="false">-->
    <security:http auto-config="true" use-expressions="false">
        <!-- 配置具体的拦截的规则 pattern="请求路径的规则" access="访问系统的人,必须有ROLE_USER的角色" -->
        <security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN"/>
        <!--<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>-->

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

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

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

    </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>

web.xml

    <!-- 配置加载类路径的配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml,classpath*:spring-security.xml</param-value>
    </context-param>
    <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提供的注解对方法来进行权限控制。
Spring Security在方法的权限控制上支持三种类型的注解:

  • JSR-250注解
  • @Secured注解
  • 支持表达式的注解

注:三种注解默认都是没有启用的,需要单独通过global-method-security元素的对应属性进行启用

开启注解使用
①SpringSecurity.xml配置文件

<security:global-method-security jsr250-annotations="enabled"/>
<security:global-method-security secured-annotations="enabled"/>
<security:global-method-security pre-post-annotations="disabled"/>

②注解开启
@EnableGlobalMethodSecurity:Spring Security默认是禁用注解的,要想开启注解,需要在继承WebSecurityConfigurerAdapter的类上加@EnableGlobalMethodSecurity注解,并在该类中将AuthenticationManager定义为Bean。

1.JSR-250注解

使用前需要添加依赖:

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

@RolesAllowed
表示访问对应方法时所应该具有的角色(标注在方法上)

@RolesAllowed({"USER", "ADMIN"})

该方法只要具有"USER", "ADMIN"任意一种权限就可以访问。

注:这里可以省略前缀ROLE_,即实际的权限是ROLE_ADMIN

@PermitAll
表示允许所有的角色进行访问,即不进行权限控制

@DenyAll
和PermitAll相反,表示无论什么角色都不能访问

当访问没有对应权限的方法时,页面报403:


image.png

可以设置错误页面:
web.xml:

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

2.@Secured注解

该注解本身就是springsecurity包下的,所以使用时不需要像JSR-250注解一样导入依赖。
@Secured注解标注的方法进行权限控制的支持,值默认为disabled
开启使用:

<security:global-method-security secured-annotations="enabled"/>

示例:

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

@Secured("ROLE_ADMIN")

注:此处不能省略前缀

3.支持表达式的注解

@PreAuthorize
在方法调用之前,基于表达式的计算结果来限制对方法的访问
开启注解使用:

<security:global-method-security pre-post-annotations="enabled" secured-annotations="enabled"/>

示例:

@PreAuthorize("#userId == authentication.principal.userId or hasAuthority('ADMIN')")
void changePassword(@P("userId") long userId ){ }

这里表示在changePassword方法执行之前:

  • 判断方法参数userId的值是否等于principal中保存的当前用户的userId
  • 当前用户是否具有ROLE_ADMIN权限

两种符合其一,就可以访问该方法。

image.png

@PostAuthorize
允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常

@PostAuthorize
User getUser("returnObject.userId == authentication.principal.userId or hasPermission(returnObject, 'ADMIN')");

@PostFilter
允许方法调用,但必须按照表达式来过滤方法的结果

@PreFilter
允许方法调用,但必须在进入方法之前过滤输入值

五、页面端标签控制权限

1.使用

在jsp页面中可以使用spring security提供的权限标签来进行权限控制
导入依赖:

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

页面导入标签:

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

2.常用标签

在jsp中可以使用三种标签:authenticationauthorizeaccesscontrollist
其中authentication代表的是当前认证对象,可以获取当前认证对象信息,例如用户名。其它两个标签可以用于权限控制
authentication
获取当前正在操作的用户信息

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

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的哪个属性中
<security:authorize access="hasRole('ADMIN')">
   <a href="${pageContext.request.contextPath}/user/findAll.do">
        <i class="fa fa-circle-o"></i> 用户管理
   </a>
</security:authorize>

注:使用了表达式,需要在springSecurity.xml配置文件中开启:


image.png

如果不开启使用表达式,可以添加配置:

<bean id="webExpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>

accesscontrollist
用于鉴定ACL权限。一共定义了三个属性:hasPermission、domainObject和var,其中前两个是必须指定的。

<security:accesscontrollist hasPermission="" domainObject="" var=""></security:accesscontrollist>
  • hasPermission:用于指定以逗号分隔的权限列表
  • domainObject:用于指定对应的域对象
  • var:用以将鉴定的结果以指定的属性名存入pageContext中,以供同一页面的其它地方使用

相关文章

网友评论

    本文标题:Spring Security入门

    本文链接:https://www.haomeiwen.com/subject/jztxzctx.html