SpringSecurity概念及整合ssm框架的示例详解

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

SpringSecurity概念及整合ssm框架的示例详解

冬竹️   2022-12-16 我要评论

基本概念

Spring中提供安全认证服务的框架,认证:验证用户密码是否正确的过程,授权:对用户能访问的资源进行控制

在这里插入图片描述

用户登录系统时我们协助 SpringSecurity 把用户对应的角色、权限组装好,同时把各个资源所要求的权限信息设定好,剩下的“登录验证”、“权限验证”等等工作都交给SpringSecurity。

权限管理过程中的相关概念

主体

英文单词:principal

使用系统的用户或设备或从其他系统远程登录的用户等等。简单说就是谁使用系统谁就是主体。

认证

英文单词:authentication

权限管理系统确认一个主体的身份,允许主体进入系统。简单说就是“主体”证明自己是谁。笼统的认为就是以前所做的登录操作。

授权

英文单词:authorization

将操作系统的“权力”“授予”“主体”,这样主体就具备了操作系统中特定功能的能力。

所以简单来说,授权就是给用户分配权限。

使用

pom文件中加入spring security的依赖

<!-- SpringSecurity 对 Web 应用进行权限管理 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<!-- SpringSecurity 配置 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<!-- SpringSecurity 标签库 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>

web.xml加入SpringSecurity控制权限的Filte

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

加入配置类

package com.atguigu.crowd.mvc.config;

import com.atguigu.crowd.constant.CrowdConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Configuration// 表示当前类是一个配置类
@EnableWebSecurity// 启用web环境下权限控制
// prePostEnabled = true的作用:保证@PreAuthorize、@PreAuthorize、@PreFilter、@PostFilter生效
@EnableGlobalMethodSecurity(prePostEnabled = true)// 启用全局方法权限控制
public class WebApplicationConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    CrowdUserDetailsService crowdUserDetailsService;
    @Autowired
    BCryptPasswordEncoder passwordEncoder;

    @Override
    protected void configure(HttpSecurity security) throws Exception {
        security
                .authorizeRequests()
                .antMatchers("/admin/to/login/page.html")
                .permitAll()// 允许无条件访问登录请求
                .antMatchers("/bootstrap/**", "/crowd/**", "/css/**",
                        "/fonts/**", "/img/**", "/jquery/**", "/layer/**",
                        "/script/**", "/ztree/**")
                .permitAll()// 允许无条件访问静态文件
                .antMatchers("/admin/get/page.html")
                .hasAuthority("user:get") // 拥有user:get权限才可以访问用户维护
                .antMatchers("/role/to/role/page.html")
                .hasRole("经理")// 拥有经理角色才可以访问角色维护
                .antMatchers("/admin/get/page.html")
                .access("(hasAnyRole('经理')) and hasAuthority('user:get')")
                .anyRequest()
                .authenticated()// 其他任何资源都要登录才可以访问
                .and()
                .exceptionHandling()
                .accessDeniedHandler(new AccessDeniedHandler() {
                    @Override
                    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                        request.setAttribute("exception", new
                                Exception(CrowdConstant.MESSAGE_ACCESS_DENIED));
                        request.getRequestDispatcher("/WEB-INF/error/system_error.jsp").forward(request, response);
                    }
                })// 出现异常的处理方法
                .and()
                .csrf()
                .disable()// 禁用跨站请求伪造功能
                .formLogin()// 开启表单登录功能
                .loginPage("/admin/to/login/page.html")// 设置默认的登录页面
                .loginProcessingUrl("/security/do/login.html")// 处理登录请求的地址
                .defaultSuccessUrl("/admin/to/admin-main/page.html")// 登录成功后要前往的地址
                .usernameParameter("loginAcct")// 账号的请求参数名
                .passwordParameter("userPswd")// 密码的请求参数名
                .and()
                .logout()
                .logoutUrl("/security/do/logout.html")// 退出登录访问的地址
                .logoutSuccessUrl("/admin/to/login/page.html")// 退出页面成功要去的地址
        ;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
//        builder
//                .inMemoryAuthentication()// 开启内存认证
//                .withUser("tom")// 设置账号
//                .password("123123")// 设置密码
//                .roles("ADMIN")// 设置角色
//        ;

        // 使用基于数据库的认证
        builder.userDetailsService(crowdUserDetailsService).passwordEncoder(passwordEncoder);
    }
}

注解@PreAuthorize规定角色权限

需要在SpringSecurity中标注

@EnableGlobalMethodSecurity(prePostEnabled = true)

    /**
     * 根据关键字查询分页信息
     *
     * @param pageNum  当前页码
     * @param pageSize 每页显示条数
     * @param keyword  关键字
     * @return 分页信息
     */
    @PreAuthorize("hasAuthority('role:get') or hasAnyRole('经理')")// 需要有role:get权限才可以访问
    @ResponseBody
    @GetMapping("/role/get/page/info.json")
    public ResultEntity<PageInfo<Role>> getPageInfo(
            @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
            @RequestParam(value = "pageSize", defaultValue = "4") Integer pageSize,
            @RequestParam(value = "keyword", defaultValue = "") String keyword
    ) {
        // 获取页面数据
        PageInfo<Role> pageInfo = roleService.getRoleByKeyword(pageNum, pageSize, keyword);
        // 如果出现异常,dispatcherServlet会进行处理
        return ResultEntity.successWithData(pageInfo);
    }

@PostAuthorize

先执行方法然后根据方法返回值判断是否具备权限。

例如:查询一个 Admin 对象,在@PostAuthorize 注解中和当前登录的 Admin 对象进行比较,如果不一致,则判断为不能访问。实现“只能查自己”效果。

@PostAuthorize("returnObject.data.loginAcct == principal.username")

使用 returnObject 获取到方法返回值,使用 principal 获取到当前登录用户的主体对象。

通过故意写错表达式,然后从异常信息中发现表达式访问的是下面这个类的属性:

org.springframework.security.access.expression.method.MethodSecurityExpressionRoot

@PreFilter

在方法执行前对传入的参数进行过滤。只能对集合类型的数据进行过滤。

@PreFilter(value="filterObject%2==0")
@ResponseBody
@RequestMapping("/admin/test/pre/filter")
public ResultEntity<List<Integer>> saveList(@RequestBody List<Integer> valueList) {
return ResultEntity.successWithData(valueList);
}

@PostFilter

在方法执行后对方法返回值进行过滤。只能对集合类型的数据进行过滤。

CrowdUserDetailsService

其实返回的就是SpringSecurity的User对象,只是做了一层封装。

package com.atguigu.crowd.mvc.config;

import com.atguigu.crowd.entity.Admin;
import com.atguigu.crowd.entity.Role;
import com.atguigu.crowd.service.api.AdminService;
import com.atguigu.crowd.service.api.AuthService;
import com.atguigu.crowd.service.api.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

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

@Component
public class CrowdUserDetailsService implements UserDetailsService {
    @Autowired
    private AdminService adminService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private AuthService authService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 1、根据账号名称查找Admin对象
        Admin admin = adminService.getAdminByLoginAcct(username);
        // 2、获取adminId
        Integer adminId = admin.getId();
        // 3、根据adminId查询角色信息
        List<Role> roles = roleService.getAssignedRole(adminId);
        // 4、根据adminId查询权限名字信息
        List<String> auths = authService.getAssignedAuthNameByAdminId(adminId);
        // 5、创建集合对象来存储GrantedAuthority
        ArrayList<GrantedAuthority> authorities = new ArrayList<>();
        // 6、遍历存入角色消息
        for (int i = 0; i < roles.size(); i++) {
            // 需要加前缀,因为SpringSecurity就是通过ROLE_来区分是角色还是权限
            String roleName = "ROLE_" + roles.get(i).getName();
            SimpleGrantedAuthority authority = new SimpleGrantedAuthority(roleName);
            authorities.add(authority);
        }
        // 7、遍历存入权限信息
        for (int i = 0; i < auths.size(); i++) {
            String authName = auths.get(i);
            SimpleGrantedAuthority authority = new SimpleGrantedAuthority(authName);
            authorities.add(authority);
        }
        // 8、封装到SecurityAdmin对象中,它继承了SpringSecurity的User类
        SecurityAdmin securityAdmin = new SecurityAdmin(admin, authorities);
        return securityAdmin;
    }
}

BCryptPasswordEncoder

它是SpringSecurity自带的密码加密与验证的类。

页面元素控制

取出当前登录对象

<%--需要导入taglib--%>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="security" %>
<%--principal 是我们自己封装的 SecurityAdmin 对象(返回的SpringSecurity的User对象)--%>
<security:authentication property="principal.originalAdmin.userName"/>

标签库控制

<%--需要导入taglib--%>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="security" %>
<security:authorize access="hasRole('经理')">
    <!-- 开始和结束标签之间是要进行权限控制的部分。检测当前用户是否有权限,有权限
    就显示这里的内容,没有权限就不显示。 --> ……
</security:authorize>

access属性可以传入权限控制相关的表达式。

### 标签库控制

```html
<%--需要导入taglib--%>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="security" %>
<security:authorize access="hasRole('经理')">
    <!-- 开始和结束标签之间是要进行权限控制的部分。检测当前用户是否有权限,有权限
    就显示这里的内容,没有权限就不显示。 --> ……
</security:authorize>

access属性可以传入权限控制相关的表达式。

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们