SpringSecurity - 简介及简单配置登录与注册

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI 和AOP(面向切面编程)功能为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。 

认证

是为用户建立一个他所声明的主体。主体一般是指用户,设备或可以在你系统中执行动作的其他系统,即登录过程

授权

指的是一个用户能否在你的应用中执行某个操作,在到达授权判断之前,身份的主题已经由身份验证过程建立了,即当前登录用户可以具有哪些权限。

依赖引入(Spring相关+SpringSecurity相关+lombok)

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

  <dependencies>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.6</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
    </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>
    <!-- SpringSecurity -->
    <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>org.springframework.security</groupId>
      <artifactId>spring-security-taglibs</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>

 SpringSecurity核心配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
        xmlns="http://www.springframework.org/schema/security"
        xmlns:beans="http://www.springframework.org/schema/beans"
        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,表示拦截请求的配置。 -->
    <!-- 不拦截静态资源,比如登录界面、注册界面、css、js等资源  -->
    <http pattern="/img/**" security="none"/>
    <http pattern="/login.html" security="none"/>

    <!--
        use-expressions:设置是否启用SpEL表达式,默认值是true
       auto-config:是否使用自带的登录界面,不写默认为false
    -->
    <http use-expressions="false">
        <!--
           配置SpringSecurity的拦截路径(拦截规则)
           - pattern:设置拦截规则
           /*  表示根路径下所有资源(不包含子路径)
             /** 表示根路径下所有资源(包含子路径)
           - access:设置角色 - 角色命名为ROLE_角色名称,如ROLE_USER
         -->
        <intercept-url pattern = "/" access = "ROLE_USER"/>
        <!--
           开启表单验证
           username-parameter = "username" 匹配前端页面表单,表单name为username
           password-parameter = "password" 匹配前端页面表单,表单name为password
             login-page:指定的登录页面
           login-processing-url:登录请求的路径
           default-target-url:登录成功(身份验证和授权)后跳转的地址
           always-use-default-target:是否登录成功之后总是跳转到default-target-url指定的地址
           authentication-failure-url:登录失败之后跳转的地址,小技巧:在地址栏中加一个参数,通过参数的值来判断是否登录成功,用于前端页面进行回显信息
           authentication-success-handler-ref:default-target-url和always-use-default-target的替代属性,通过该属性直接跳到一个调度的Action,由这个Action去进行跳转(适用场景:当业务需要根据权限跳转到不同页面时)
         -->
        <form-login
                login-page = "/login.html"
                login-processing-url = "/login"
                default-target-url = "/index.hmtl"
                always-use-default-target = "true"
                authentication-failure-url = "/login.html?loginError=true"
                authentication-success-handler-ref = "loginSuccessHandler"
        />

        <!-- 不使用csrf校验 -->
        <csrf disabled="true"/>

        <!-- 框架页面不拦截,即html页面可以使用iframe和frame标签 -->
        <headers>
            <frame-options policy="SAMEORIGIN" />
        </headers>

        <!--
            注销配置
            logout-url:注销请求的路径
            logout-success-url:注销成功跳转的地址
            invalidate-session:是否销毁Session
         -->
        <logout logout-url="/logout" logout-success-url="/login.html" invalidate-session="true" />

        <!-- 记住我,免密登录,前端表单name为remember-me -->
        <remember-me />


    </http>

    <!-- 配置认证管理器 -->
    <authentication-manager>
        <!--
            认证的提供者
             user-service-ref:自定义用户认证信息
         -->
        <authentication-provider user-service-ref="userDetailService">
            <!-- 在认证权限管理器当中指定加密工具类 -->
            <password-encoder ref="passwordEncoder" />
        </authentication-provider>
    </authentication-manager>

    <!-- 自定义用户认证信息,自定义认证类需要实现UserDetailsService接口 -->
    <beans:bean id="userDetailService" class="pers.liuchengyin.service.UserDetailServiceImpl"></beans:bean>

    <!-- 加密工具类,对MD5进行随机加盐 -->
    <beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" ></beans:bean>

    <!-- 登录成功,处理登录成功的Action,实现类需要实现AuthenticationSuccessHandler接口 -->
    <beans:bean id="loginSuccessHandler" class="pers.liuchengyin.service.AuthenticationSuccessHandlerImpl" ></beans:bean>

</beans:beans>

配置文件中是通过authentication-success-handler-ref来进行处理登录成功的,因此这里给一个loginSuccessHandler的示例。该类实现了AuthenticationSuccessHandler接口。这个类的主要作用就是用来登录成功之后处理一些跳转之前的事情,或是根据角色权限来进行不同的跳转。

public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
    // 该方法会在登录成功时调用 - 在这个方法里可以通过判断角色的不同,拥有的权限不同,进行不同的跳转
    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException {
        System.out.println("登录成功");
        // 这里要进行手动跳转
        httpServletResponse.sendRedirect("/index.html");
    }
}

配置文件中是自定义认证类来实现登录的,即可以从数据库中获取数据。这里给一个UserDetailServiceImpl的示例,该类实现了UserDetailsService接口。这个类里有一个loadUserByUsername的方法,它会在登录时被调用。

public class UserDetailServiceImpl implements UserDetailsService {
    /**
     * 该方法会在登录时调用 - 这里通常就是用来从数据库中获取角色和权限的
     * @param username 用户名
     * @return User对象
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 判断用户名是否为空
        if(username == null || "".equals(username)){
            return null;
        }
        // 定义权限的集合
        List<GrantedAuthority> authorityList =  new ArrayList<>();
        // 从数据库中读取角色 - 这里直接写出来的
        authorityList.add(new SimpleGrantedAuthority("ROLE_USER"));
        // 从数据库中读取用户对象 - 这里直接写出来
        Role role = new Role(username,"1234");
        // 判断role是否为空
        if(role != null){
            // 密码前拼接上{noop}  表示不加密:{noop}1234
            User user = new User(username,role.getPassword(),authorityList);
            return user;
        }
        return null;
    }
}

既然登录有了,那么注册是怎么做的呢?请看下面这个类

public class RoleController {

    @Autowired
    private RoleDao roleDao;

    @Autowired
    private PasswordEncoder passwordEncoder;

    public void add(Role role){
      // 获取明文密码
      String password = role.getPassword();
      // 对明文密码进行加密
      String securityPassword = passwordEncoder.encode(password);
      // 把加密后的密码存储到role对象中
      role.setPassword(securityPassword);
      // 调用数据库添加到数据库去
      roleDao.add(role);
}

那么它内部是怎么加密的呢?其实就是通过BCryptpassword这个类生成随机的盐然后对原密码混淆。

具体,我这里就不谈了,看下面这篇文章,我也是看了这篇文章才明白的。

https://blog.csdn.net/Leo_songHJ/article/details/83277350

发布了59 篇原创文章 · 获赞 13 · 访问量 2537

猜你喜欢

转载自blog.csdn.net/qq_40885085/article/details/102774436