springboot整合security实现权限控制

1.建表,五张表,如下:
1.1.用户表
CREATE TABLE `t_sys_user` (
`user_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`user_name` varchar(30) NOT NULL COMMENT '用户名',
`user_password` varchar(128) NOT NULL COMMENT '用户密码',
`salt` varchar(64) DEFAULT NULL COMMENT '加密盐',
`user_phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`user_emai` varchar(20) DEFAULT NULL COMMENT '邮箱',
`user_title` varchar(20) DEFAULT NULL COMMENT '职称',
`creater_id` bigint(20) DEFAULT NULL COMMENT '创建人ID',
`creater_name` varchar(30) DEFAULT NULL COMMENT '创建人名称',
`creater_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater_id` bigint(20) DEFAULT NULL COMMENT '更新人ID',
`updater_name` varchar(30) DEFAULT NULL COMMENT '更新人名称',
`updater_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`role_ids` varchar(200) DEFAULT NULL,
`role_names` varchar(300) DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;

1.2.用户角色表
CREATE TABLE `t_sys_user_role` (
`user_role_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户角色ID',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`role_id` bigint(20) NOT NULL COMMENT '角色ID',
PRIMARY KEY (`user_role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8;


1.3.角色表
CREATE TABLE `t_sys_role` (
`role_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
`role_name` varchar(100) NOT NULL COMMENT '角色名称',
`role_code` varchar(100) NOT NULL COMMENT '角色编码',
`creater_id` bigint(20) DEFAULT NULL COMMENT '创建人ID',
`creater_name` varchar(30) DEFAULT NULL COMMENT '创建人名称',
`creater_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater_id` bigint(20) DEFAULT NULL COMMENT '更新人ID',
`updater_name` varchar(30) DEFAULT NULL COMMENT '更新人名称',
`updater_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`permission_ids` varchar(200) DEFAULT NULL,
`permission_names` varchar(300) DEFAULT NULL,
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;


1.4.角色权限表
CREATE TABLE `t_sys_role_permission` (
`role_permission_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色权限ID',
`role_id` bigint(20) NOT NULL COMMENT '角色ID',
`permission_id` bigint(20) NOT NULL COMMENT '权限ID',
PRIMARY KEY (`role_permission_id`)
) ENGINE=InnoDB AUTO_INCREMENT=78 DEFAULT CHARSET=utf8;


1.5.权限表

CREATE TABLE `t_sys_permission` (
`permission_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '权限ID',
`permission_name` varchar(100) NOT NULL COMMENT '权限名称',
`permission_code` varchar(100) NOT NULL COMMENT '权限编码',
`creater_id` bigint(20) DEFAULT NULL COMMENT '创建人ID',
`creater_name` varchar(30) DEFAULT NULL COMMENT '创建人名称',
`creater_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater_id` bigint(20) DEFAULT NULL COMMENT '更新人ID',
`updater_name` varchar(30) DEFAULT NULL COMMENT '更新人名称',
`updater_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`permission_id`)
) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8;


2.pom.xml引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

3.编码步骤:
3.1.在用户实体类中实现UserDetails接口的方法

package com.lz.hehuorenservice.system.entity;

import com.lz.hehuorenservice.common.entity.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.*;

/** Create by hyhweb on 2021/6/6 16:24 */
public class User extends BaseEntity implements UserDetails {

/** 用户主键ID */
@ApiModelProperty(value = "用户主键ID")
private Long userId;
/** 用户名 */
@ApiModelProperty(value = "用户名")
private String userName;
/** 用户密码 */
@ApiModelProperty(value = "用户密码")
private String userPassword;

@ApiModelProperty(value = "")
private String salt;
/** 手机号 */
@ApiModelProperty(value = "手机号")
private String userPhone;
/** 邮箱 */
@ApiModelProperty(value = "邮箱")
private String userEmai;
/** 职称 */
@ApiModelProperty(value = "职称")
private String userTitle;

@ApiModelProperty(value = "角色ID")
private String roleIds;

@ApiModelProperty(value = "角色名称")
private String roleNames;
/** 创建人ID */
@ApiModelProperty(value = "创建人ID")
private Long createrId;
/** 创建人名称 */
@ApiModelProperty(value = "创建人名称")
private String createrName;
/** 创建时间 */
@ApiModelProperty(value = "创建时间")
private Date createrTime;
/** 更新人ID */
@ApiModelProperty(value = "更新人ID")
private Long updaterId;
/** 更新人名称 */
@ApiModelProperty(value = "更新人名称")
private String updaterName;
/** 更新时间 */
@ApiModelProperty(value = "更新时间")
private Date updaterTime;

private Set<String> permissions;

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
/*
//绑定角色的授权方法
if(roles !=null){
for (Role sysRole : roles) {
authorities.add(new SimpleGrantedAuthority(sysRole.getRoleCode()));
}
}*/

// 绑定权限的授权方法
if (permissions != null) {
for (String permission : permissions) {
authorities.add(new SimpleGrantedAuthority(permission));
}
}

return authorities;
}

@Override
public String getPassword() {
return userPassword;
}

@Override
public String getUsername() {
return userName;
}

@Override
public boolean isAccountNonExpired() {
return true;
}

@Override
public boolean isAccountNonLocked() {
return true;
}

@Override
public boolean isCredentialsNonExpired() {
return true;
}

@Override
public boolean isEnabled() {
return true;
}

public Long getUserId() {
return userId;
}

public void setUserId(Long userId) {
this.userId = userId;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getUserPassword() {
return userPassword;
}

public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}

public String getSalt() {
return salt;
}

public void setSalt(String salt) {
this.salt = salt;
}

public String getUserPhone() {
return userPhone;
}

public void setUserPhone(String userPhone) {
this.userPhone = userPhone;
}

public String getUserEmai() {
return userEmai;
}

public void setUserEmai(String userEmai) {
this.userEmai = userEmai;
}

public String getUserTitle() {
return userTitle;
}

public void setUserTitle(String userTitle) {
this.userTitle = userTitle;
}

public String getRoleIds() {
return roleIds;
}

public void setRoleIds(String roleIds) {
this.roleIds = roleIds;
}

public String getRoleNames() {
return roleNames;
}

public void setRoleNames(String roleNames) {
this.roleNames = roleNames;
}

public Long getCreaterId() {
return createrId;
}

public void setCreaterId(Long createrId) {
this.createrId = createrId;
}

public String getCreaterName() {
return createrName;
}

public void setCreaterName(String createrName) {
this.createrName = createrName;
}

public Date getCreaterTime() {
return createrTime;
}

public void setCreaterTime(Date createrTime) {
this.createrTime = createrTime;
}

public Long getUpdaterId() {
return updaterId;
}

public void setUpdaterId(Long updaterId) {
this.updaterId = updaterId;
}

public String getUpdaterName() {
return updaterName;
}

public void setUpdaterName(String updaterName) {
this.updaterName = updaterName;
}

public Date getUpdaterTime() {
return updaterTime;
}

public void setUpdaterTime(Date updaterTime) {
this.updaterTime = updaterTime;
}

public Set<String> getPermissions() {
return permissions;
}

public void setPermissions(Set<String> permissions) {
this.permissions = permissions;
}
}

 

3.2.在用户的服务实现类中,实现UserDetailsService接口的loadUserByUsername方法,返回用户的所有信息。

package com.lz.hehuorenservice.system.service.impl;

import com.lz.hehuorenservice.common.service.impl.BaseServiceImpl;
import com.lz.hehuorenservice.system.dao.UserDao;
import com.lz.hehuorenservice.system.entity.User;
import com.lz.hehuorenservice.system.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
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.Service;

import java.util.Set;

/** Create by hyhweb on 2021/6/6 16:28 */
@Service
public class UserServiceImpl extends BaseServiceImpl<User, Long>
implements UserService, UserDetailsService {
@Autowired UserDao userDao;

@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
User user = userDao.getUserByName(userName);
if (user == null) {
throw new UsernameNotFoundException("账户不存在");
}
Set<String> permissions = userDao.getPermissionByUserId(user.getUserId());
user.setPermissions(permissions);
return user;
}
}

3.3.编写配置类,重写WebSecurityConfigurerAdapter类的三个configure方法,也就是重新配置三个对象AuthenticationManagerBuilder,HttpSecurity,WebSecurity。

package com.lz.hehuorenservice.common.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lz.hehuorenservice.common.bean.CustomAccessDeniedHandler;
import com.lz.hehuorenservice.common.bean.CustomAuthenticationEntryPoint;
import com.lz.hehuorenservice.common.filter.CustomAuthenticationFilter;
import com.lz.hehuorenservice.system.entity.User;
import com.lz.hehuorenservice.system.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.web.cors.CorsUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/** Create by hyhweb on 2021/6/7 8:26 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired UserServiceImpl userService; // 这个必须是接口的实现类,不能是接口

@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
// return NoOpPasswordEncoder.getInstance();
}

/* @Bean
RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
// String hierarchy = "ROLE_dba> ROLE_admin \n ROLE_admin > ROLE_user";
String hierarchy = "ROLE_admin > ROLE_user";
roleHierarchy.setHierarchy(hierarchy);
return roleHierarchy;
}*/

@Bean
CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
filter.setAuthenticationSuccessHandler(
new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(
HttpServletRequest req, HttpServletResponse resp, Authentication auth)
throws IOException, ServletException {
Object principal = auth.getPrincipal();
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
resp.setStatus(200);
Map<String, Object> map = new HashMap<>();
map.put("code", "1");
map.put("success", true);
map.put("message", "登录成功");
User user = (User) principal;
user.setUserPassword(null);
map.put("data", user);
ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close();
/* resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String,Object> map = new HashMap<String,Object>();
map.put("message", "登录成功");
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();*/

}
});
filter.setAuthenticationFailureHandler(
new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(
HttpServletRequest req, HttpServletResponse resp, AuthenticationException e)
throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
resp.setStatus(401);
Map<String, Object> map = new HashMap<>();
map.put("status", 401);
if (e instanceof LockedException) {
map.put("msg", "账号被锁定,登录失败");
} else if (e instanceof BadCredentialsException) {
map.put("msg", "账号或密码输入错误,请重新登录");
} else if (e instanceof DisabledException) {
map.put("msg", "账号被禁用,登录失败");
} else if (e instanceof AccountExpiredException) {
map.put("msg", "账号过期,登录失败");
} else if (e instanceof CredentialsExpiredException) {
map.put("msg", "密码过期,登录失败");
} else {
map.put("msg", "登录失败");
}
ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close();
/*resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String,Object> map = new HashMap<String,Object>();
map.put("message", "登录失败");
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();*/
}
});
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}

@Bean
public AccessDeniedHandler getAccessDeniedHandler() {
return new CustomAccessDeniedHandler();
}

@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/sessionInvalid", "/register", "/app/**", "/login_page")
.antMatchers("/index.html", "/static/**", "/favicon.ico")
.antMatchers(
"/swagger-ui/**",
"/swagger/**",
"/doc.html",
"/swagger-resources/**",
"/images/**",
"/webjars/**",
"/v3/api-docs",
"/configuration/ui",
"/configuration/security");
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors() // 开启跨域
.and() // 获取一个安全编译器
.authorizeRequests() // 授权请求
.requestMatchers(CorsUtils::isPreFlightRequest)
.permitAll() // 跨域的请求开放所有权限
.anyRequest() // 所有请求
.authenticated() // 所有请求都需要认证
.and()
.sessionManagement()
.invalidSessionUrl("/session/invalid")
.and()
// 获取一个安全编译器
.formLogin()
// 表单登录配置
.loginPage("/login_page")
// 登录页面访问地址
.loginProcessingUrl("/login")
// 配置登录接口地址
.usernameParameter("userName")
// 配置登录的账号字段
.passwordParameter("userPassWord")
// 配置登录密码字段
.and()
// 获取一个安全编译器
.logout()
// 退出登录配置
.logoutUrl("/logout")
// 设置退出登录的接口地址
.clearAuthentication(true)
// 清除所有认证信息
.invalidateHttpSession(true)
// 让session失效
.addLogoutHandler(
new LogoutHandler() {
// 退出登录时的处理器
@Override
public void logout(
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Authentication authentication) {}
})
.logoutSuccessHandler(
new LogoutSuccessHandler() {
// 退出成功后的处理器
@Override
public void onLogoutSuccess(
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Authentication authentication)
throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
Map<String, Object> map = new HashMap<>();
map.put("message", "退出成功");
map.put("code", "1");
map.put("success", true);
ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close();
}
})
.permitAll() // 设置退出登录的所有权限
.and() // 获取一个安全编译器
.csrf()
.disable() // 关闭csrf跨站点请求伪造
.exceptionHandling()
.authenticationEntryPoint(new CustomAuthenticationEntryPoint());
// 自定义认证的入口异常处理方法
http.addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
// 重写用户名密码的过滤器,实现前后端分离获取登录的用户名,密码信息
http.exceptionHandling().accessDeniedHandler(getAccessDeniedHandler());
// 没有权限访问的处理器
}
}

3.3.1CustomAccessDeniedHandler自定义没权限方法的处理器

package com.lz.hehuorenservice.common.bean;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
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;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/** Create by hyhweb on 2021/6/7 11:50 */
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
AccessDeniedException e)
throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
Map map = new HashMap<>();
map.put("message", "权限不足,请联系管理员开通权限");
map.put("code", 0);
map.put("status", 403);
map.put("success", false);
String result = new ObjectMapper().writeValueAsString(map);
out.write(result);
out.flush();
out.close();
}
}


3.3.2CustomAuthenticationEntryPoint自定义认证的入口
package com.lz.hehuorenservice.common.bean;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/** Create by hyhweb on 2021/6/7 11:42 */
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
AuthenticationException e)
throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
Map map = new HashMap<>();
map.put("message", "还没登录,请重新登录");
map.put("code", 302);
String result = new ObjectMapper().writeValueAsString(map);
out.write(result);
out.flush();
out.close();
}
}

3.3.3.CustomAuthenticationFilter自定义

package com.lz.hehuorenservice.common.filter;

import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

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

/** Create by hyhweb on 2021/6/7 12:07 */
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(
HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)
|| request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
UsernamePasswordAuthenticationToken authRequest = null;
try (InputStream is = request.getInputStream()) {
ObjectMapper mapper = new ObjectMapper();
Map<String, String> authenticationBean = mapper.readValue(is, Map.class);
authRequest = new UsernamePasswordAuthenticationToken(
authenticationBean.get("userName"), authenticationBean.get("userPassWord"));
/* authRequest =
new UsernamePasswordAuthenticationToken(
request.getParameter("userName"), request.getParameter("userPassWord"));*/
} catch (IOException e) {
e.printStackTrace();
authRequest = new UsernamePasswordAuthenticationToken("", "");
} finally {
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
} else {
return super.attemptAuthentication(request, response);
}
}
}

 

4.controller层使用权限注释@PreAuthorize实现权限控制

@RestController
@RequestMapping("/user")
@Api(tags = "用户信息")
public class UserController{
@Autowired private UserService userService;

@ApiOperation(value = "删除单个对象", notes = "删除单个对象接口")
@GetMapping("/delete/{id}")
@PreAuthorize("hasAuthority('delete')")
public ApiResult deleteById(@PathVariable long id) {
return userService.deleteById(id);
}
}

 

附加说明:
Spring Security的表达式对象的基类:
org.springframework.security.access.expression.SecurityExpressionRoot
在controller的方法中使用注释,如下:
@PreAuthorize(“表达式(‘权限值’)”)

@PreAuthorize("hasAuthority('zixunguanli-xinzeng')")
public ApiResult add(@RequestBody String json) {
return infoService.add(JSON.parseObject(json, InfoReq.class));
}

表达式如下:

boolean hasAuthority(String var1);

boolean hasAnyAuthority(String... var1);

boolean hasRole(String var1);

boolean hasAnyRole(String... var1);

boolean permitAll();

boolean denyAll();

boolean isAnonymous();

boolean isAuthenticated();

boolean isRememberMe();

boolean isFullyAuthenticated();

boolean hasPermission(Object var1, Object var2);

boolean hasPermission(Object var1, String var2, Object var3);

Spring Security的重构获取用户名和密码的方式,实现前后端分离的json格式,如下:
重构org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter的attemptAuthentication方法

spring boot集成spring security(jwt+redis有完整源码)

一、Spring Security官方解释
Spring Security是一个强大的和高度可定制的身份验证和访问控制框架。它是保证基于spring的应用程序安全的实际标准。Spring Security是一个框架,着重于为Java应用程序提供身份验证和授权。春天像所有项目,Spring Security的真正力量是很容易找到的它可以扩展以满足定制需求。

本项目使用jwt当作token,使用redis存储token,登录信息不依赖于单个项目,集中存储到redis内存型数据库中。

二、spring boot集成步骤(完整项目地址:https://gitee.com/fds520/spring-security-demo)
1. 项目结构

2.核心代码

主配置类SpringSecurityConfig
package com.fds.system.config;

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * spring security 主配置类
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Autowired
    private SecurityProperties securityProperties;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 禁用 csrf 拦截
        http.csrf().disable()
                .sessionManagement()
                // 关闭session管理,使用token机制处理
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                // 未登录返回 JSON 格式的数据
                .httpBasic().authenticationEntryPoint((httpServletRequest, httpServletResponse, e) -> {
                    httpServletRequest.setCharacterEncoding("utf-8");
                    httpServletResponse.setHeader("Content-type", "application/json;charset=UTF-8");
                    httpServletResponse.getWriter().write(JSON.toJSONString("未登录"));
                })
                .and()
                // 无权访问 JSON 格式的数据
                .exceptionHandling().accessDeniedHandler((httpServletRequest, httpServletResponse, e) -> {
                    httpServletResponse.setCharacterEncoding("utf-8");
                    httpServletResponse.setHeader("Content-type", "application/json;charset=UTF-8");
                    httpServletResponse.getWriter().write(JSON.toJSONString("没有权限"));
                })
                .and()
                .authorizeRequests()
                // 对option不校验
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                // 设置不校验白名单
                .antMatchers(securityProperties.getIgnoreUrl()).permitAll()
                .anyRequest().authenticated()
                .and()
                // 添加自定义请求jwt过滤器
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    public void configure(WebSecurity web) {
        // 设置拦截忽略文件夹,可以对静态资源放行
        web.ignoring().antMatchers("/images/**");
    }
}
登录service
/**
     * 获取数据库用户
     *
     * @param username 用户名
     * @return
     * @throws UsernameNotFoundException
     */
    public String login(String username, String password) {
        // 模拟从数据库 获取登录用户
        LoginUser loginUser = new LoginUser("fds", "123");
        loginUser.setId(123L);
        loginUser.setType("people");
        Set authoritiesSet = new HashSet();
        // 模拟从数据库中获取用户权限
        authoritiesSet.add("test:add");
        authoritiesSet.add("test:list");
        authoritiesSet.add("ddd:list");
        loginUser.setCustomAuthorities(authoritiesSet);
        String token = JwtTokenUtil.generateToken(loginUser);
        redisUtil.set(token, JSONObject.toJSONString(loginUser), securityProperties.getExpirationMilliSeconds());
        return token;
    }
token过滤器
package com.fds.system.config;

import com.alibaba.fastjson.JSONObject;
import com.fds.system.model.LoginUser;
import com.fds.system.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * @author: fds
 * @description: jwt-token过滤器
 */
@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String authToken = request.getHeader("Authorization");
        response.setCharacterEncoding("utf-8");
        if (StringUtils.isEmpty(authToken)) {
            // 用户未登录
            filterChain.doFilter(request, response);
            return;
        }
        // 获取redis中的token信息
        if (!redisUtil.hasKey(authToken)) {
            // 用户未登录
            filterChain.doFilter(request, response);
            return;
        }

        Object data = redisUtil.get(authToken);
        if (null == data) {
            // 用户未登录
            filterChain.doFilter(request, response);
            return;
        }

        // 获取缓存中的信息(根据自己的业务进行拓展)
        LoginUser loginUser = JSONObject.parseObject(data.toString(), LoginUser.class);
        // 设置权限
        loginUser.setSystemAuthorities();
        // 从tokenInfo中取出用户信息
        // 更新token过期时间
        redisUtil.setKeyExpire(authToken, securityProperties.getExpirationMilliSeconds());
        // 将信息交给security
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        filterChain.doFilter(request, response);
    }
}

  


 

posted @ 2022-05-06 08:52  hanease  阅读(1566)  评论(0编辑  收藏  举报