• 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);
        }
    }
    

      

    
    
    
  • 相关阅读:
    MySQL Show Processlist 整理
    由linkedin数据库泄漏引发的思考[转发]
    MySQL 严重 Bug 用户登陆漏洞[转发]
    关于博客的开始
    Linux共享库位置配置
    getopt详解
    git使用
    ArrayList读源码心得
    HashMap内部实现机制及优化第一篇
    如何才能做一个好网站系列 第三篇 最初的思路
  • 原文地址:https://www.cnblogs.com/hanease/p/16227267.html
Copyright © 2020-2023  润新知