从数据库查询权限信息与自定义失败处理
从数据库查询权限信息
代码实现
我们只需要根据用户id去查询到其所对应的权限信息即可。
所以我们可以先定义个mapper,其中提供一个方法可以根据userid查询权限信息。
package com.example.qinghuatokendemo.Mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.qinghuatokendemo.Domain.Menu; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface MenuMapper extends BaseMapper<Menu> { List<String> selectPermsByUserId(Long userid); }
尤其是自定义方法,所以需要创建对应的mapper文件,定义对应的sql语句
<?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.example.qinghuatokendemo.Mapper.MenuMapper"> <select id="selectPermsByUserId" resultType="java.lang.String"> SELECT DISTINCT m.`perms` FROM sys_user_role ur LEFT JOIN `sys_role` r ON ur.`role_id` = r.`id` LEFT JOIN `sys_role_menu` rm ON ur.`role_id` = rm.`role_id` LEFT JOIN `sys_menu` m ON m.`id` = rm.`menu_id` WHERE user_id = #{userid} AND r.`status` = 0 AND m.`status` = 0 </select> </mapper>
在application.yml中配置mapperXML文件的位置
spring: datasource: url: jdbc:mysql://localhost:3306/springsecurity?characterEncoding=utf-8&serverTimezone=UTC username: root password: njzyb555 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml
测试类
package com.example.qinghuatokendemo; import com.example.qinghuatokendemo.Domain.User; import com.example.qinghuatokendemo.Mapper.MenuMapper; import com.example.qinghuatokendemo.Mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import java.util.List; @SpringBootTest public class MapperTest { @Autowired private UserMapper userMapper; @Autowired private MenuMapper menuMapper; @Autowired private PasswordEncoder passwordEncoder; @Test public void TestBCryptPasswordEncoder(){ //$2a$10$9CmQULPcw8prFL.gnmM/zO1bDtPtVNb4mTxNs2wHsm7xonGMCvT2C System.out.println(passwordEncoder. matches("1234", "$2a$10$eAQvguaa3mHMt7cUrXeQnu3vIw74tbNtthm/t1gH6IMrRihv1OpRu")); /*String encode = passwordEncoder.encode("1234"); System.out.println(encode);*/ } @Test public void testUser(){ List<User> users = userMapper.selectList(null); System.out.println(users); } @Test public void testselectPermsByUserId(){ List<String> list = menuMapper.selectPermsByUserId(1L); System.out.println(list); } }
自定义失败处理
我们还希望在认证失败或者是授权失败的情况下也能和我们的接口一样返回相同结构的json,这样可以让前端能对响应进行统一的处理。要实现这个功能我们需要知道SpringSecurity的异常处理机制。
在SpringSecurity中,如果我们在认证或者授权的过程中出现了异常会被ExceptionTranslationFilter捕获到。在ExceptionTranslationFilter中会去判断是认证失败还是授权失败出现的异常。
如果是认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理。
如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。
所以如果我们需要自定义异常处理,我们只需要自定义AuthenticationEntryPoint和AccessDeniedHandler然后配置给SpringSecurity即可。
自定义实现类
package com.example.qinghuatokendemo.Handler; import com.alibaba.fastjson.JSON; import com.example.qinghuatokendemo.Domain.ResponseResult; import com.example.qinghuatokendemo.Utils.WebUtils; import org.springframework.http.HttpStatus; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class AccessDeniedHandlerImpl implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { ResponseResult result = new ResponseResult(HttpStatus.INTERNAL_SERVER_ERROR.value(),"用户认证失败请查询登录"); String json = JSON.toJSONString(request); //处理异常 WebUtils.renderString(response,json); } }
package com.example.qinghuatokendemo.Handler; import com.alibaba.fastjson.JSON; import com.example.qinghuatokendemo.Domain.ResponseResult; import com.example.qinghuatokendemo.Utils.WebUtils; import org.springframework.http.HttpStatus; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class AccessDeniedHandlerImpl implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { ResponseResult result = new ResponseResult(HttpStatus.FORBIDDEN.value(), "权限不足"); String json = JSON.toJSONString(result); WebUtils.renderString(response,json); } }
package com.example.qinghuatokendemo.Config; import com.example.qinghuatokendemo.Handler.AccessDeniedHandlerImpl; import com.example.qinghuatokendemo.filter.JwtAuthenticationTokenFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; 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.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.AuthenticationEntryPoint; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Autowired JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; @Autowired private AuthenticationEntryPoint authenticationEntryPoint; @Autowired private AccessDeniedHandler accessDeniedHandler; @Override protected void configure(HttpSecurity http) throws Exception { /* http //关闭csrf .csrf().disable() //不通过Session获取SecurityContext .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() // 对于登录接口 允许匿名访问 .antMatchers("/user/login").anonymous() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated();*/ http .csrf().disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/hello").permitAll() .antMatchers("/user/login").anonymous() .anyRequest().authenticated(); //把token校验过滤器添加到过滤器链中 http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); //配置异常处理器 http.exceptionHandling() //认证失败处理器 .authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }