SpringSecurity概念及整合ssm框架的示例详解
冬竹️ 人气:0基本概念
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属性可以传入权限控制相关的表达式。
加载全部内容