今日指数项目集成SpringSecurity

项目集成SpringSecurity

​ 在第一章我们是基于SpringSecurity、JWT技术实现前后端无状态化认证授权,而我们当前的项目是前后端分离的架构,同样也可借助Security框架和Jwt实现前后端的无状态认证授权操作;

1、项目自定义认证过滤器

1.1 依赖导入

在stock_backend工程导入SpringSecurity启动依赖:

<!--引入security-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>

1.2 自定义认证过虑器

当前项目中认证登录信息的合法性,除了用户名、密码外,还需要校验验证码,所以认证过滤器需要注入redis模板对象:

package com.itheima.stock.security.filter;import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;
import com.itheima.stock.constant.StockConstant;
import com.itheima.stock.security.detail.LoginUserDetail;
import com.itheima.stock.utils.JwtTokenUtil;
import com.itheima.stock.vo.req.LoginReqVo;
import com.itheima.stock.vo.resp.LoginRespVoExt;
import com.itheima.stock.vo.resp.R;
import com.itheima.stock.vo.resp.ResponseCode;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;/*** @author by itheima* @Date 2022/7/14* @Description*/
public class JwtLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {private RedisTemplate redisTemplate;/*** 通过setter方法注解redis模板对象* @param redisTemplate*/public void setRedisTemplate(RedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}/*** 通过构造器传入自定义的登录地址* @param loginUrl*/public JwtLoginAuthenticationFilter(String loginUrl) {super(loginUrl);}/*** 用户认证处理的方法* @param request* @param response* @return* @throws AuthenticationException* @throws IOException* @throws ServletException* 我们约定请求方式必须是post方式,且请求的数据时json格式*              约定请求是账户key:username  密码:password*/@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {//判断请求方法必须是post提交,且提交的数据的内容必须是application/json格式的数据if (!request.getMethod().equals("POST") ||! (request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_UTF8_VALUE))) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}//获取请求参数//获取reqeust请求对象的发送过来的数据流ServletInputStream in = request.getInputStream();//将数据流中的数据反序列化成MapLoginReqVo vo = new ObjectMapper().readValue(in, LoginReqVo.class);response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.setCharacterEncoding("utf-8");//1.判断参数是否合法if (vo==null || StringUtils.isBlank(vo.getUsername())|| StringUtils.isBlank(vo.getPassword())|| StringUtils.isBlank(vo.getSessionId()) || StringUtils.isBlank(vo.getCode())) {R<Object> resp = R.error(ResponseCode.USERNAME_OR_PASSWORD_ERROR.getMessage());response.getWriter().write(new ObjectMapper().writeValueAsString(resp));return null;}//从程序执行的效率看,先进行校验码的校验,成本较低String rCheckCode =(String) redisTemplate.opsForValue().get(StockConstant.CHECK_PREFIX + vo.getSessionId());if (rCheckCode==null || ! rCheckCode.equalsIgnoreCase(vo.getCode())) {//响应验证码输入错误R<Object> resp = R.error(ResponseCode.CHECK_CODE_ERROR.getMessage());response.getWriter().write(new ObjectMapper().writeValueAsString(resp));return null;}String username = vo.getUsername();//username = (username != null) ? username : "";username = username.trim();String password = vo.getPassword();//password = (password != null) ? password : "";//将用户名和密码信息封装到认证票据对象下UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);// Allow subclasses to set the "details" property//setDetails(request, authRequest);//调用认证管理器认证指定的票据对象return this.getAuthenticationManager().authenticate(authRequest);}
}

2、自定义用户详情服务

​ 上一小结,我们完成了认证过滤器的开发,认证过程中认证管理器AutenticationManager底层会调用用户详情服务对象获取用户详情信息,所以接下来我们需要实现用户详情服务;

权限表注意事项:

在这里插入图片描述

2.1 自定义UserDetail认证详情信息类

package com.itheima.stock.security.detail;import com.itheima.stock.vo.resp.PermissionRespNodeVo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;
import java.util.List;/*** @author by itheima* @Date 2022/7/14* @Description 自定义用户认证详情类*/
@Data//setter getter toString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class LoginUserDetail implements UserDetails {/*** 用户名称*/private String username;
//    @Override
//    public String getUsername() {
//        return null;
//    }/*** 密码*/private String password;
//    @Override
//    public String getPassword() {
//        return null;
//    }/*** 权限信息*/private List<GrantedAuthority> authorities;
//    @Override
//    public Collection<? extends GrantedAuthority> getAuthorities() {
//        return null;
//    }/*** 账户是否过期*/private boolean isAccountNonExpired=true;
//    @Override
//    public boolean isAccountNonExpired() {
//        return false;
//    }/*** 账户是否被锁定*  true:没有被锁定*/private boolean isAccountNonLocked=true;
//    @Override
//    public boolean isAccountNonLocked() {
//        return false;
//    }/*** 密码是否过期*  true:没有过期*/private boolean isCredentialsNonExpired=true;
//    @Override
//    public boolean isCredentialsNonExpired() {
//        return false;
//    }/*** 账户是否禁用*  true:没有禁用*/private boolean isEnabled=true;
//    @Override
//    public boolean isEnabled() {
//        return false;
//    }/*** 用户ID*/private String id;/*** 电话*/private String phone;/*** 昵称*/private String nickName;/*** 真实姓名*/private String realName;/*** 性别(1.男 2.女)*/private Integer sex;/*** 账户状态(1.正常 2.锁定 )*/private Integer status;/*** 邮箱*/private String email;/*** 权限树,不包含按钮相关权限信息*/private List<PermissionRespNodeVo> menus;/*** 按钮权限树*/private List<String> permissions;
}

2.2 自定义UserDetailsService实现

package com.itheima.stock.security.service;import com.google.common.base.Strings;
import com.itheima.stock.mapper.SysPermissionMapper;
import com.itheima.stock.mapper.SysRoleMapper;
import com.itheima.stock.mapper.SysUserMapperExt;
import com.itheima.stock.pojo.entity.SysPermission;
import com.itheima.stock.pojo.entity.SysRole;
import com.itheima.stock.pojo.entity.SysUser;
import com.itheima.stock.security.detail.LoginUserDetail;
import com.itheima.stock.service.PermissionService;
import com.itheima.stock.vo.resp.PermissionRespNodeVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.List;
import java.util.stream.Collectors;/*** @author by itheima* @Date 2022/7/13* @Description 定义获取用户合法详情信息的服务*/
@Component
public class LoginUserDetailService implements UserDetailsService {@Autowiredprivate SysUserMapperExt sysUserMapperExt;@Autowiredprivate SysPermissionMapper sysPermissionMapper;@Autowiredprivate SysRoleMapper sysRoleMapper;@Autowiredprivate PermissionService permissionService;/*** 当用户登录认证是,底层会自动调用MyUserDetailService#loadUserByUsername()把登录的账户名称传入* 根据用户名称获取用户的详情信息:用户名 加密密码 权限集合,还包含前端需要的侧边栏树 、前端需要的按钮权限标识的集合等* @param loginName* @return* @throws UsernameNotFoundException*/@Overridepublic UserDetails loadUserByUsername(String loginName) throws UsernameNotFoundException {//2.根据用户名查询用户信息SysUser dbUser= sysUserMapperExt.findUserByUserName(username);//3.判断查询的用户信息if (dbUser==null) {throw new UsernameNotFoundException("用户不存在");}//4.2 成功则返回用户的正常信息//获取指定用户的权限集合 添加获取侧边栏数据和按钮权限的结合信息List<SysPermission> permissions = permissionService.getPermissionByUserId(dbUser.getId());//前端需要的获取树状权限菜单数据List<PermissionRespNodeVo> tree = permissionService.getTree(permissions, 0l, true);//前端需要的获取菜单按钮集合List<String> authBtnPerms = permissions.stream().filter(per -> !Strings.isNullOrEmpty(per.getCode()) && per.getType() == 3).map(per -> per.getCode()).collect(Collectors.toList());//5.组装后端需要的权限标识//5.1 获取用户拥有的角色List<SysRole> roles = sysRoleMapper.getRoleByUserId(dbUser.getId());//5.2 将用户的权限标识和角色标识维护到权限集合中List<String> perms=new ArrayList<>();permissions.stream().forEach(per->{if (StringUtils.isNotBlank(per.getPerms())) {perms.add(per.getPerms());}});roles.stream().forEach(role->{perms.add("ROLE_"+role.getName());});String[] permStr=perms.toArray(new String[perms.size()]);//5.3 将用户权限标识转化成权限对象集合List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList(permStr);//6.封装用户详情信息实体对象LoginUserDetail loginUserDetail = new LoginUserDetail();//将用户的id nickname等相同属性信息复制到详情对象中BeanUtils.copyProperties(dbUser,loginUserDetail);loginUserDetail.setMenus(tree);loginUserDetail.setAuthorities(authorityList);loginUserDetail.setPermissions(authBtnPerms);return loginUserDetail;}
}

2.3 完善相关mapper

A.定义根据用户名查询用户信息的接口方法

在SysUserMapper定义方法:

    /*** 根据用户名查询用户信息* @param username* @return*/SysUser findUserByUserName(@Param("username") String username);

绑定xml:

    <select id="findUserByUserName" resultMap="BaseResultMap">select <include refid="Base_Column_List"/> from sys_user where username=#{username}</select>

B.定义根据用户id查询角色信息的接口方法

在SysRoleMapper定义方法:

    /*** 根据用户id查询角色信息* @param userId* @return*/List<SysRole> getRoleByUserId(@Param("userId") Long userId);

绑定xml:

<select id="getRoleByUserId" resultMap="BaseResultMap">SELECTr.*FROMsys_user_role AS ur,sys_role AS rWHEREur.role_id = r.idAND ur.user_id = #{userId}
</select>

C.定义根据用户id查询权限信息的接口方法

在SysPermissionMapper定义方法:

    /*** 根据用户id查询用户信息* @param userId* @return*/List<SysPermission> getPermissionByUserId(@Param("userId") Long userId);

绑定xml:

    <select id="getPermissionByUserId" resultMap="BaseResultMap">SELECTdistinct  p.*FROMsys_role_permission AS rp,sys_user_role AS ur,sys_permission AS pWHEREur.role_id = rp.role_idAND rp.permission_id = p.idAND ur.user_id = #{userId}</select>

3、认证成功后响应Token实现

在认证成功方法successfulAuthentication中,基于jwt将用户信息和相关的权限信息通过jwt加密响应给前端请求:

public class JwtLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {//省略N行代码....../*** 用户认证成功后回调的方法* 认证成功后,响应前端token信息* @param request* @param response* @param chain security的过滤器链* @param authResult* @throws IOException* @throws ServletException*/@Overrideprotected void successfulAuthentication(HttpServletRequest request,HttpServletResponse response,FilterChain chain,Authentication authResult) throws IOException, ServletException {//获取用户的详情信息LoginUserDetail userDetail = (LoginUserDetail) authResult.getPrincipal();//组装LoginRespVoExt//获取用户名称String username = userDetail.getUsername();//获取权限集合对象List<GrantedAuthority> authorities = userDetail.getAuthorities();String auStrList = authorities.toString();//复制userDetail属性值到LoginRespVoExt对象即可LoginRespVoExt resp = new LoginRespVoExt();BeanUtils.copyProperties(userDetail,resp);//生成token字符串:将用户名称和权限信息价格生成token字符串String tokenStr = JwtTokenUtil.createToken(username, auStrList);resp.setAccessToken(tokenStr);//封装统一响应结果R<Object> r = R.ok(resp);String respStr = new ObjectMapper().writeValueAsString(r);response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.setCharacterEncoding("UTF-8");response.getWriter().write(respStr);}/*** 认证失败后,回调的方法* @param request* @param response* @param failed* @throws IOException* @throws ServletException*/@Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {R<Object> r = R.error(ResponseCode.SYSTEM_PASSWORD_ERROR);String respStr = new ObjectMapper().writeValueAsString(r);response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.setCharacterEncoding("UTF-8");response.getWriter().write(respStr);}

4、定义Security配置类

过程同第一章节大致一样,需要注意swagger访问、knif4j、验证码等资源设置pertmall访问权限;

package com.itheima.stock.security.config;import com.itheima.stock.security.filter.JwtAuthorizationFilter;
import com.itheima.stock.security.filter.JwtLoginAuthenticationFilter;
import com.itheima.stock.security.handler.StockAccessDenyHandler;
import com.itheima.stock.security.handler.StockAuthenticationEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
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.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;/*** @author by itheima* @Date 2022/7/14* @Description*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig  extends WebSecurityConfigurerAdapter {@Autowiredprivate RedisTemplate redisTemplate;/*** 定义公共的无需被拦截的资源* @return*/private String[] getPubPath(){//公共访问资源String[] urls = {"/**/*.css","/**/*.js","/favicon.ico","/doc.html","/druid/**","/webjars/**","/v2/api-docs","/api/captcha","/swagger/**","/swagger-resources/**","/swagger-ui.html"};return urls;}/*** 配置过滤规则* @param http* @throws Exception*/@Overrideprotected void configure(HttpSecurity http) throws Exception {//登出功能http.logout().logoutUrl("/api/logout").invalidateHttpSession(true);//开启允许iframe 嵌套。security默认禁用ifram跨域与缓存http.headers().frameOptions().disable().cacheControl().disable();//session禁用http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);http.csrf().disable();//禁用跨站请求伪造http.authorizeRequests()//对资源进行认证处理.antMatchers(getPubPath()).permitAll()//公共资源都允许访问.anyRequest().authenticated();  //除了上述资源外,其它资源,只有 认证通过后,才能有权访问//自定义的过滤器http.addFilterBefore(jwtLoginAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);}/*** 自定义认证过滤器bean* @return* @throws Exception*/@Beanpublic JwtLoginAuthenticationFilter jwtLoginAuthenticationFilter() throws Exception {JwtLoginAuthenticationFilter filter = new JwtLoginAuthenticationFilter("/api/login");filter.setAuthenticationManager(authenticationManagerBean());filter.setRedisTemplate(redisTemplate);return filter;}/*** 定义密码加密器,实现对明文密码的加密和匹配操作* @return*/@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}
}

注意:认证过滤器配置完毕后,需要注释掉原来的登录入口;

断点调试认证流程:

​ 略;

5、自定义授权过滤器

授权的逻辑与第一章相似,需要实现OncePerRequestFilter过滤器;

5.1 定义授权顾虑器

package com.itheima.stock.security.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.stock.constant.StockConstant;
import com.itheima.stock.utils.JwtTokenUtil;
import com.itheima.stock.vo.resp.R;
import com.itheima.stock.vo.resp.ResponseCode;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
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;
import java.util.List;/*** @author by itheima* @Date 2022/7/14* @Description 定义授权过滤器,核心作用:*      1.过滤请求,获取请求头中的token字符串*      2.解析token字符串,并获取token中信息:username role*      3.将用户名和权限信息封装到UsernamePassowrdAuthentionToken票据对象下*      4.将票据对象放入安全上下文,方便校验权限时,随时获取*/
public class JwtAuthorizationFilter extends OncePerRequestFilter {/*** 访问过滤的方法* @param request* @param response* @param filterChain 过滤器链* @throws ServletException* @throws IOException*/@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain) throws ServletException, IOException {//1.从request对象下获取token数据,约定key:AuthorizationString tokenStr = request.getHeader(StockConstant.TOKEN_HEADER);//判断token字符串是否存在if (tokenStr==null) {//如果票据为null,可能用户还没有认证,正准备去认证,所以放行请求//放行后,会不会访问当受保护的资源呢?不会,因为没有生成UsernamePassowrdAuthentionTokenfilterChain.doFilter(request,response);return;}//2.解析tokenStr,获取用户详情信息Claims claims = JwtTokenUtil.checkJWT(tokenStr);//token字符串失效的情况if (claims==null) {//说明 票据解析出现异常,票据就失效了R<Object> r = R.error(ResponseCode.TOKEN_NO_AVAIL);String respStr = new ObjectMapper().writeValueAsString(r);response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.setCharacterEncoding("UTF-8");response.getWriter().write(respStr);return;}//获取用户名和权限信息String userName = JwtTokenUtil.getUsername(tokenStr);//生成token时,权限字符串的格式是:[P8,ROLE_ADMIN]String perms = JwtTokenUtil.getUserRole(tokenStr);//生成权限集合对象//P8,ROLE_ADMINString strip = StringUtils.strip(perms, "[]");List<GrantedAuthority> authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList(strip);//将用户名和权限信息封装到token对象下UsernamePasswordAuthenticationToken token=new UsernamePasswordAuthenticationToken(userName,null,authorityList);//将token对象存入安全上限文,这样,线程无论走到哪里,都可以获取token对象,验证当前用户访问对应资源是否被授权SecurityContextHolder.getContext().setAuthentication(token);//资源发行filterChain.doFilter(request,response);}
}

5.2 配置授权过滤器

在SecurityConfig类中配置授权过滤器:

    /*** 配置过滤规则* @param http* @throws Exception*/@Overrideprotected void configure(HttpSecurity http) throws Exception {//......省略//配置授权过滤器,过滤一切资源http.addFilterBefore(jwtAuthorizationFilter(),JwtLoginAuthenticationFilter.class);}/*** 自定义授权过滤器* @return*/@Beanpublic JwtAuthorizationFilter jwtAuthorizationFilter(){return new JwtAuthorizationFilter();}

授权访问测试;

​ 略

6、定义权限拒绝处理器

  • 定义用户认证成功无权限访问处理器
package com.itheima.stock.security.handler;import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.stock.vo.resp.R;
import com.itheima.stock.vo.resp.ResponseCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
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;/*** @author by itheima* @Date 2022/7/14* @Description 定义没有权限,访问拒绝的处理器* 用户认证成功,但是没有访问的权限,则会除非拒绝处理器* 如果是匿名用户访问被拒绝则使用匿名拒绝的处理器*/
@Slf4j
public class StockAccessDenyHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request,HttpServletResponse response,AccessDeniedException ex) throws IOException, ServletException {log.info("访问拒绝,异常信息:{}",ex.getMessage());//说明 票据解析出现异常,票据就失效了R<Object> r = R.error(ResponseCode.NOT_PERMISSION);String respStr = new ObjectMapper().writeValueAsString(r);response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.setCharacterEncoding("UTF-8");response.getWriter().write(respStr);}
}
  • 匿名用户(未认证用户)访问拒绝处理器
package com.itheima.stock.security.handler;import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.stock.vo.resp.R;
import com.itheima.stock.vo.resp.ResponseCode;
import org.springframework.http.MediaType;
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;/*** @author by itheima* @Date 2022/7/14* @Description*  未认证的用户访问被拒绝的处理器*/
public class StockAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request,HttpServletResponse response,AuthenticationException authException) throws IOException, ServletException {//说明 票据解析出现异常,票据就失效了R<Object> r = R.error(ResponseCode.NOT_PERMISSION);String respStr = new ObjectMapper().writeValueAsString(r);response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.setCharacterEncoding("UTF-8");response.getWriter().write(respStr);}
}
  • 配置类配置处理器

在SecurityConfig类配置处理器:

    /*** 配置过滤规则* @param http* @throws Exception*/@Overrideprotected void configure(HttpSecurity http) throws Exception {//省略......//配置权限访问拒绝处理器http.exceptionHandling().accessDeniedHandler(new StockAccessDenyHandler()).authenticationEntryPoint(new StockAuthenticationEntryPoint());}

测试拒绝处理器:

​ 略

7、配置资源访问权限注解

为接口添加权限注解,比如:

@RestController
@RequestMapping("/api")
public class LogController {@Autowiredprivate LogService logService;/*** 日志信息综合查询* @param vo* @return*/@PreAuthorize("hasAuthority('sys:log:list')")@PostMapping("/logs")public R<PageResult> logPageQuery(@RequestBody LogPageReqVo vo){return logService.logPageQuery(vo);}/*** 批量删除日志信息功能* @param logIds* @return*/@DeleteMapping("/log")@PreAuthorize("hasAuthority('sys:log:delete')")public R<String> deleteBatch(@RequestBody List<Long> logIds){return this.logService.deleteBatch(logIds);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/56941.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

特斯拉Robotaxi发布会2024:自动驾驶未来的开端

引言 2024年10月&#xff0c;特斯拉在洛杉矶举行了一场引发全球科技界高度关注的发布会&#xff0c;主题为“We Robot”。这场发布会展示了特斯拉的最新自动驾驶技术&#xff0c;包括无人驾驶出租车Cybercab和无人驾驶厢式货车Robovan&#xff0c;并且还展示了人形机器人Optim…

D43【python 接口自动化学习】- python基础之函数

day43 装饰器&#xff08;上&#xff09; 学习日期&#xff1a;20241020 学习目标&#xff1a;函数&#xfe63;- 56 装饰器&#xff1a;函数嵌套的定义与调用的区别 学习笔记&#xff1a; 变量作用域 变量读取顺序&#xff1a;local-》enclosed-》global-》builtin # 变量…

c语言经典100例

1.字符串转为数字 #include <stdio.h>int strToInt(char *s) {int num0;int sign1;int step1;if (*s -){sign -1;s;}while (*s > 0&&*s < 9){num num*10(*s-0);step 10;s;}return num*sign; }int main() {char a[10] "-1234";char *s a ;pr…

数据库知识点整理

DDL DDL-数据库操作 show databases ------------ 查看所有数据库 select database(); ----------查看当前数据库 create database 数据库名&#xff1b;---- 创建数据库 use 数据库名&#xff1b; --------------使用数据库 drop database 数据库名&#xff1b;--…

自由学习记录(11)

Surface Effector 2D Platform Effector 2D 向上跳跃穿过天花板的功能 平台效应器不用变Trigger&#xff0c;因为本来就是要有碰撞的 use one way grouping是让所有相关联的碰撞器都可以单面跳墙 默认不勾选&#xff0c;左右两边没有摩擦力和弹力&#xff0c;要自己先设置sid…

poisson过程——随机模拟(Python和R实现)

Python实现 exponential()使用&#xff0c;自动poisson过程实现。 import numpy as np import matplotlib.pyplot as plt# Parameters lambda_rate 5 # rate parameter (events per time unit) T 10 # total time# Generate Poisson process times np.random.exponential(…

SpringBoot+MyBatis+MySQL项目基础搭建

一、新建项目 1.1 新建springboot项目 新建项目 选择SpringBoot&#xff0c;填写基本信息&#xff0c;主要是JDK版本和项目构建方式&#xff0c;此处以JDK17和Maven举例。 1.2 引入依赖 选择SpringBoot版本&#xff0c;勾选Lombok&#xff0c;Spring Web&#xff0c;MyBa…

数据结构与算法:高级数据结构与实际应用

目录 14.1 跳表 14.2 Trie树 14.3 B树与 B树 14.4 其他高级数据结构 总结 数据结构与算法&#xff1a;高级数据结构与实际应用 本章将探讨一些高级数据结构&#xff0c;这些数据结构在提高数据存取效率和解决复杂问题上起到重要作用。这些高级数据结构包括跳表&#xff0…

【2022统考真题】计算时间复杂度

目录 一、题目描述 二、思路分析 三、易错提醒 四、同级和嵌套的关系 一、题目描述 下列程序段的时间复杂度是&#xff08;&#xff09; int sum 0; for (int i 1; i < n; i * 2) for (int j 0; j < i; j) sum; A. O(logn) B. O(n) C. O(nlogn) D…

【工欲善其事】巧用 PowerShell 自动清除复制 PDF 文本时夹杂的换行符号

文章目录 巧用 PowerShell 自动清除复制 PDF 文本时夹杂的换行符号1 问题描述2 解决方案3 具体步骤4 效果测试5 小结与复盘 巧用 PowerShell 自动清除复制 PDF 文本时夹杂的换行符号 1 问题描述 不知各位是否也为复制过来的文本中夹杂的回车换行符抓狂过&#xff1f;就是在复…

作业2-线性回归的Matlab代码实现

一、前言 相关配置&#xff1a;Matlab 2020a&#xff08;版本的影响应该不大&#xff0c;.m代码基本都能运行&#xff0c;个人感觉就是Simulink对版本的要求高一些&#xff09; 二、任务描述 基于近两节课的理论推导&#xff0c;用代码实现线性回归&#xff0c;并对预测结果进…

基于SpringBoot+Vue+uniapp微信小程序的澡堂预订的微信小程序的详细设计和实现

项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不是配置文件。Spring Boot 通过自动化配置和约…

目标检测系统【环境详细配置过程】(CPU版本)

&#xff08;如果你使用的是笔记本电脑&#xff0c;没有比较好的GPU&#xff0c;可以配置CPU运行环境&#xff09; 链接&#xff1a;上百种【基于YOLOv8/v10/v11的目标检测系统】目录&#xff08;pythonpyside6界面系统源码可训练的数据集也完成的训练模型&#xff09; 1.安装…

MySQL中什么情况下类型转换会导致索引失效

文章目录 1. 问题引入2. 准备工作3. 案例分析3.1 正常情况3.2 发生了隐式类型转换的情况 4. MySQL隐式类型转换的规则4.1 案例引入4.2 MySQL 中隐式类型转换的规则4.3 验证 MySQL 隐式类型转换的规则 5. 总结 如果对 MySQL 索引不了解&#xff0c;可以看一下我的另一篇博文&…

CTF(七)

导言&#xff1a; 本文主要讲述在CTF竞赛中&#xff0c;Misc&#xff08;杂项&#xff09;题目中的CatchCat。 靶场链接&#xff1a;攻防世界 (xctf.org.cn) 一&#xff0c;分析题目。 下载并解压附件&#xff1a; CatchCat.txt文件内容为&#xff1a; 发现是坐标。 另一个…

表贴式永磁同步电机无感控制-基于滑膜观测器SMO法

导读:在全速域范围内,一般的永磁同步电机无感控制要分为低速区域和高速区域两个部分。原因在于常规的方法是利用模型建立反电动势观测器来求解转子位置信息,但其只适合在中高速区域。本文介绍一种基于滑膜观测器SMO法+PLL的方法。 需要文章中的仿真,关注微信公众号:浅谈电…

python机器人编程——用python调用API控制wifi小车的实例程序

目录 一、前言二、一个客户端的简单实现2.1 首先定义一个类及属性2.2 其次定义连接方法2.3 定义一些回调函数2.4 定义发送小车指令方法2.5 定义一个正常关闭方法 三、python编程控制小车的demo实现四、小结PS.扩展阅读ps1.六自由度机器人相关文章资源ps2.四轴机器相关文章资源p…

【大数据技术基础 | 实验四】HDFS实验:读写HDFS文件

文章目录 一、实验目的二、实验要求三、实验原理&#xff08;一&#xff09;Java Classpath&#xff08;二&#xff09;Eclipse Hadoop插件 四、实验环境五、实验内容和步骤&#xff08;一&#xff09;配置master服务器classpath&#xff08;二&#xff09;使用master服务器编写…

JVM学习总结:字节码篇

本文是学习尚硅谷宋红康老师主讲的 尚硅谷JVM精讲与GC调优教程 的总结 &#xff0c;部分内容也参考了 JavaGuide 网站&#xff08;文末有链接&#xff09; JVM 概述 Oracle JDK 与 OpenJDK 是什么关系&#xff1f; 2006 年 SUN 公司将 Java 开源&#xff0c;也就有了 OpenJDK。…

Games202作业5(完结)

单帧降噪 也就是针对图像空间进行降噪 也就是我们需要在像素&#xff08;i,j&#xff09;的四周进行采样&#xff0c;然后将采样的权重加到一起&#xff0c;然后所有的权重和像素的乘积也加到一起&#xff0c;然后相除&#xff0c;得到最终滤波后的(i,j)像素期望的结果。 Buf…