SpringSecurity
权限管理概述
作为权限管理框架,其内部机制可分为两大部分,其一是认证授权auhorization,其二是权限校验authentication。
认证授权authorization是指,根据用户提供的身份凭证,生成权限实体,并为之授予相应的权限。
权限校验authentication是指,用户请求访问被保护资源时,将被保护资源所需的权限和用户权限实体所拥护的权限二者进行比对,如果校验通过则用户可以访问被保护资源,否则拒绝访问。
Spring 权限框架介绍
Spring Security 简介
Spring 是一个非常流行和成功的 Java 应用开发框架。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。一般来说,Web 应用的安全性包括用户认(Authentication)和用户授权(Authorization)两个部分。用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。
SpringSecurity入门案例
(一)表结构分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tueKqa6u-1657025853878)(E:\Java资料\笔记整理\JavaUp\笔记整理\SpringSecurity_权限管理.assets\22.png)]
(二)构建工程添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
(三)引入状态码和结果集常量工具类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UZ3zH4es-1657025853880)(E:\Java资料\笔记整理\JavaUp\笔记整理\SpringSecurity_权限管理.assets\image-20220705155809537.png)]
ResultCode接口
package com.chenshuang.commons;
/**
* @ClassName ResultCode
* @Description 统一返回状态码
* @Author liucaijing
* @Date 2020/10/10:00
* @Version 1.0
*/
public interface ResultCode {
/**成功*/
public static Integer SUCCESS = 20000;
/**失败*/
public static Integer ERROR = 20001;
/**未授权(匿名)*/
public static Integer UNAUTHORIZED_01 = 20002;
/**未授权(认证后)*/
public static Integer UNAUTHORIZED_02 = 20003;
}
统一返回的结果类ResultData
package com.chenshuang.commons;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: lcj
* @Date: 2020/10/27 10:31
* @Description: 统一返回结果类
* @Version: 0.0.1
*/
@Data
public class ResultData {
/**是否成功*/
private Boolean success;
/**返回编码*/
private Integer code;
/**返回消息*/
private String message;
/**返回数据*/
private Map<String, Object> data = new HashMap<String, Object>();
public ResultData() {
}
/**
* 成功静态方法
* @return
*/
public static ResultData ok() {
ResultData result = new ResultData();
result.setSuccess(true);
result.setCode(ResultCode.SUCCESS);
result.setMessage("成功!");
return result;
}
/**
* 成功静态方法
* @return
*/
public static ResultData ok(Integer code, String message) {
ResultData result = new ResultData();
result.setSuccess(true);
result.setCode(code);
result.setMessage(message);
return result;
}
/**
* 失败静态方法
* @return
*/
public static ResultData error() {
ResultData result = new ResultData();
result.setSuccess(false);
result.setCode(ResultCode.ERROR);
result.setMessage("失败!");
return result;
}
/**
* 失败静态方法
* @return
*/
public static ResultData error(Integer code, String message) {
ResultData result = new ResultData();
result.setSuccess(false);
result.setCode(code);
result.setMessage(message);
return result;
}
public ResultData success(Boolean success){
this.setSuccess(success);
return this;
}
public ResultData message(String message){
this.setMessage(message);
return this;
}
public ResultData code(Integer code){
this.setCode(code);
return this;
}
public ResultData data(String key, Object value){
this.data.put(key, value);
return this;
}
public ResultData data(Map<String, Object> map){
this.setData(map);
return this;
}
}
(四)创建验证失败工具类SecurityAuthenticationFailureHandler
@Component
public class SecurityAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write(JSON.toJSONString(ResultData.error(ResultCode.ERROR, "登录失败!")));
}
}
(五)创建匿名访问无权限处理类AuthenticationEntryPointHandler
@Component
public class AuthenticationEntryPointHandler implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write(JSON.toJSONString(ResultData.error(ResultCode.UNAUTHORIZED_01, "没有权限访问!")));
}
}
(六) 创建认证用户无权访问工具类SecurityAccessDeniedHandler
@Component
public class SecurityAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write(JSON.toJSONString(ResultData.error(ResultCode.UNAUTHORIZED_02, "没有权限访问!")));
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sc3Iy62p-1657025853881)(E:\Java资料\笔记整理\JavaUp\笔记整理\SpringSecurity_权限管理.assets\image-20220705160130942.png)]
(七)构建SpringSecurity的配置工具类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TKjaJb9F-1657025853882)(E:\Java资料\笔记整理\JavaUp\笔记整理\SpringSecurity_权限管理.assets\image-20220705160107895.png)]
package com.chenshuang.config;
import com.chenshuang.handler.AuthenticationEntryPointHandler;
import com.chenshuang.handler.SecurityAccessDeniedHandler;
import com.chenshuang.handler.SecurityAuthenticationFailureHandler;
import org.springframework.context.annotation.Bean;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.annotation.Resource;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private SecurityAuthenticationFailureHandler authenticationFailureHandler;
@Resource
private AuthenticationEntryPointHandler authenticationEntryPoint;
@Resource
private SecurityAccessDeniedHandler accessDeniedHandler;
@Resource
private UserDetailsService userDetailsService;
/**
* 配置密码解析
* @return
*/
@Bean
protected PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 配置用户名和密码
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
protected void configure(HttpSecurity http) throws Exception {
//禁用iframe框架
http.headers().frameOptions().disable();
//关闭csrf防护 跨站请求防护
http.csrf().disable()
//表单登录
.formLogin()
//登录页面
.loginPage("/index.html")
//登录访问路径,与页面表单提交路径一致
.loginProcessingUrl("/checkLogin")
//登录成功后访问路径
.defaultSuccessUrl("/pages/main.html").permitAll()
//登录失败操作
.failureHandler(authenticationFailureHandler)
.and()
//认证配置
.authorizeRequests()
.antMatchers("/index.html", "/checkLogin").permitAll()
//配置静态页面可以访问
.antMatchers("/js/**", "/css/**", "/img/**","/plugins/**", "/loginstyle/**","/favicon.ico").permitAll()
//任何请求
.anyRequest()
//都需要身份验证
.authenticated();
//配置无权限访问页面
//http.exceptionHandling().accessDeniedPage("/uanuth.html");
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler);
//配置退出
http.logout()
//退出路径
.logoutUrl("/logout")
//退出后跳转页面
.logoutSuccessUrl("/index.html");
}
}
(八)UserDetailsServiceImpl认证授权工具类
package com.chenshuang.config;
import com.chenshuang.pojo.Permission;
import com.chenshuang.pojo.Role;
import com.chenshuang.pojo.User;
import com.chenshuang.service.UserService;
import org.apache.dubbo.config.annotation.Reference;
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 MyUserDetailsService implements UserDetailsService {
@Reference
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findUserByUsername(username);
if (user==null){
throw new UsernameNotFoundException("用户不存在");
}
ArrayList<GrantedAuthority> authorityList = new ArrayList<>();
//查询用户角色
List<Role> roleList = userService.findRoleByUserId(user.getId());
if(roleList!=null && roleList.size()>0){
for (Role role : roleList) {
SimpleGrantedAuthority simpleGrantedAuthority=new SimpleGrantedAuthority(role.getKeyword());
authorityList.add(simpleGrantedAuthority);
}
}
//查询权限
List<Permission> permissionList = userService.findUserPermissionByUserId(user.getId());
if(permissionList!=null && permissionList.size()>0){
for (Permission permission : permissionList) {
SimpleGrantedAuthority simpleGrantedAuthority=new SimpleGrantedAuthority(permission.getKeyword());
authorityList.add(simpleGrantedAuthority);
}
}
return new org.springframework.security.core.userdetails.User(user.getUsername(),user.getPassword(),authorityList);
}
}
(九)业务层查询
Service接口
public interface UserService {
User findUserByUsername(String username);
List<Role> findRoleByUserId(Integer id);
List<Permission> findUserPermissionByUserId(Integer id);
}
Service实现类
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Override
public User findUserByUsername(String username) {
QueryWrapper<User> userQueryWrapper=new QueryWrapper<>();
userQueryWrapper.eq("username",username);
return userMapper.selectOne(userQueryWrapper);
}
@Override
public List<Role> findRoleByUserId(Integer id) {
return userMapper.findRoleByUserId(id);
}
@Override
public List<Permission> findUserPermissionByUserId(Integer id) {
return userMapper.findUserPermissionByUserId(id);
}
}
userMapper接口
public interface UserMapper extends BaseMapper<User> {
public List<Role> findRoleByUserId(int id);
public List<Permission> findUserPermissionByUserId(int id);
}
userMapper.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chenshuang.mapper.UserMapper">
<select id="findRoleByUserId" parameterType="int" resultType="Role">
SELECT * FROM t_role WHERE id IN(SELECT role_id FROM t_user_role WHERE user_id=#{id})
</select>
<select id="findUserPermissionByUserId" parameterType="int" resultType="Permission">
SELECT * FROM t_permission WHERE id IN(
SELECT permission_id FROM t_role_permission
WHERE role_id IN(
SELECT id FROM t_role
WHERE id IN(
SELECT role_id FROM t_user_role
WHERE user_id=#{id})))
</select>
</mapper>
(十)controller层 @PreAuthorize设置权限
@RestController
@RequestMapping("/checkitem")
public class CheckitemController {
@Reference
private CheckitemService checkitemService;
@PreAuthorize("hasAuthority('CHECKITEM_QUERY')")
@RequestMapping("/findcheckitems")
// @RequestBody 表示请求参数为json格式
public PageResult findcheckitems(@RequestBody QueryPageBean queryPageBean){
return checkitemService.findCheckItems(queryPageBean);
}
@PreAuthorize("hasAuthority('CHECKITEM_ADD')")
@RequestMapping("/saveCheckItem")
public Result saveCheckItem(@RequestBody Checkitem checkitem){
return checkitemService.saveCheckItem(checkitem);
}
@PreAuthorize("hasAuthority('CHECKITEM_EDIT')")
@RequestMapping("/changeCheckItem")
public Result changeCheckItem(@RequestBody Checkitem checkitem){
return checkitemService.changeCheckItem(checkitem);
}
@PreAuthorize("hasAuthority('CHECKITEM_DELETE')")
@RequestMapping("/delCheckItem")
public Result delCheckItem(@RequestBody Checkitem checkitem){
return checkitemService.delCheckItem(checkitem);
}
@RequestMapping("/findAllcheckitem")
public List<Checkitem> findAllcheckitem(){
return checkitemService.findAllcheckitem();
}
}
changeCheckItem")
public Result changeCheckItem(@RequestBody Checkitem checkitem){
return checkitemService.changeCheckItem(checkitem);
}
@PreAuthorize("hasAuthority('CHECKITEM_DELETE')")
@RequestMapping("/delCheckItem")
public Result delCheckItem(@RequestBody Checkitem checkitem){
return checkitemService.delCheckItem(checkitem);
}
@RequestMapping("/findAllcheckitem")
public List<Checkitem> findAllcheckitem(){
return checkitemService.findAllcheckitem();
}
}