• 前后端分离--前置路由守卫(登录过滤)和整合shiro安全框架


    前置路由守卫:就是在路由跳转前加上自己得一些业务代码。

     添加配置

    1. router.beforeEach((to, from, next) => {
    2. var path = to.path;
    3. if(path==="/login"){
    4. return next();
    5. }
    6. var token = sessionStorage.getItem("token");
    7. if(token){
    8. return next();
    9. }
    10. return next("/login")
    11. })

    整合shiro安全框架

    添加依赖

    1. <dependency>
    2. <groupId>org.apache.shirogroupId>
    3. <artifactId>shiro-spring-boot-starterartifactId>
    4. <version>1.7.0version>
    5. dependency>

    添加shiro配置类

    1. import com.ytr.system.filter.Myfilter;
    2. import com.ytr.system.realm.MyRealm;
    3. import org.apache.shiro.authc.credential.CredentialsMatcher;
    4. import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    5. import org.apache.shiro.realm.Realm;
    6. import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    7. import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    8. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    9. import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    10. import org.springframework.boot.web.servlet.FilterRegistrationBean;
    11. import org.springframework.context.annotation.Bean;
    12. import org.springframework.context.annotation.Configuration;
    13. import org.springframework.web.filter.DelegatingFilterProxy;
    14. import javax.servlet.Filter;
    15. import java.util.HashMap;
    16. /**
    17. * @author Yang
    18. * @ClassName myConfig
    19. * @date 2022/8/7 14:44
    20. */
    21. @Configuration
    22. public class ShiroConfig {
    23. /*配置shiro管理器*/
    24. @Bean
    25. public DefaultWebSecurityManager securityManager() {
    26. DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
    27. /*设置读取的自定义的relam*/
    28. defaultWebSecurityManager.setRealm(realm());
    29. return defaultWebSecurityManager;
    30. }
    31. /*创建realm配置自定义的relam*/
    32. @Bean
    33. public Realm realm() {
    34. MyRealm myRealm = new MyRealm();
    35. /*设置密码加密器 调用自定义的加密器*/
    36. myRealm.setCredentialsMatcher(credentialsMatcher());
    37. return myRealm;
    38. }
    39. /*设置密码加密器配置*/
    40. @Bean
    41. public CredentialsMatcher credentialsMatcher() {
    42. /*创建密码加密器*/
    43. HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
    44. /*设置加密形式*/
    45. credentialsMatcher.setHashAlgorithmName("MD5");
    46. /*设置加密次数*/
    47. credentialsMatcher.setHashIterations(1024);
    48. return credentialsMatcher;
    49. }
    50. /*创建过滤器*/
    51. @Bean(value = "shiroFilter")
    52. public ShiroFilterFactoryBean filterFactoryBean() {
    53. ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
    54. factoryBean.setSecurityManager(securityManager());
    55. //设置拦截规则
    56. HashMap map = new HashMap<>();
    57. map.put("/system/login", "anon");
    58. map.put("/**", "authc");
    59. //放行Swagger2页面,需要放行这些
    60. map.put("/swagger-ui.html", "anon");
    61. map.put("/swagger/**", "anon");
    62. map.put("/webjars/**", "anon");
    63. map.put("/swagger-resources/**", "anon");
    64. map.put("/v2/**", "anon");
    65. map.put("/static/**", "anon");
    66. map.put("/doc.html", "anon");
    67. map.put("/swagger2/**", "anon");
    68. factoryBean.setFilterChainDefinitionMap(map);
    69. //设置自定义认证过滤器
    70. HashMap filterMap = new HashMap();
    71. filterMap.put("authc", new Myfilter());
    72. factoryBean.setFilters(filterMap);
    73. return factoryBean;
    74. }
    75. @Bean //注册filter
    76. public FilterRegistrationBean filterRegistrationBean() {
    77. FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>();
    78. filterRegistrationBean.setName("shiroFilter");
    79. filterRegistrationBean.setFilter(new DelegatingFilterProxy());
    80. filterRegistrationBean.addUrlPatterns("/*");
    81. return filterRegistrationBean;
    82. }
    83. //开启shiro注解
    84. @Bean
    85. public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
    86. AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
    87. authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
    88. return authorizationAttributeSourceAdvisor;
    89. }
    90. @Bean
    91. public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
    92. DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
    93. advisorAutoProxyCreator.setProxyTargetClass(true);
    94. return advisorAutoProxyCreator;
    95. }
    96. /* @Bean
    97. //LifecycleBeanPostProcessor 是管理shiro生命周期的
    98. //这个方法不加也可以
    99. public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
    100. return new LifecycleBeanPostProcessor();
    101. }*/
    102. }

    添加过滤器

    1. import com.fasterxml.jackson.databind.ObjectMapper;
    2. import com.ytr.system.vo.CommonResult;
    3. import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
    4. import javax.servlet.ServletRequest;
    5. import javax.servlet.ServletResponse;
    6. import java.io.PrintWriter;
    7. /**
    8. * @author Yang
    9. * @ClassName Myfilter
    10. * @date 2022/8/7 10:41
    11. */
    12. public class Myfilter extends FormAuthenticationFilter {
    13. @Override
    14. //未登录时进入该方法
    15. protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
    16. response.setContentType("application/json;charset=utf-8");
    17. PrintWriter writer = response.getWriter();
    18. CommonResult commonResult = new CommonResult(3000, "未登录", null);
    19. ObjectMapper objectMapper = new ObjectMapper();
    20. String json = objectMapper.writeValueAsString(commonResult);
    21. writer.print(json);
    22. writer.flush();
    23. writer.close();
    24. return false;
    25. }
    26. }

    添加自定义relam

    1. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    2. import com.ytr.system.entity.User;
    3. import com.ytr.system.service.IUserService;
    4. import org.apache.shiro.authc.AuthenticationException;
    5. import org.apache.shiro.authc.AuthenticationInfo;
    6. import org.apache.shiro.authc.AuthenticationToken;
    7. import org.apache.shiro.authc.SimpleAuthenticationInfo;
    8. import org.apache.shiro.authz.AuthorizationInfo;
    9. import org.apache.shiro.authz.SimpleAuthorizationInfo;
    10. import org.apache.shiro.realm.AuthorizingRealm;
    11. import org.apache.shiro.subject.PrincipalCollection;
    12. import org.apache.shiro.subject.Subject;
    13. import org.apache.shiro.util.ByteSource;
    14. import org.springframework.beans.factory.annotation.Autowired;
    15. import java.util.List;
    16. /**
    17. * @author Yang
    18. * @ClassName MyRealm
    19. * @date 2022/8/7 14:54
    20. */
    21. public class MyRealm extends AuthorizingRealm {
    22. @Autowired
    23. private IUserService iUserService;
    24. /*授权方法 当执行权限校验时执行此方法*/
    25. @Override
    26. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    27. return null;
    28. }
    29. /*认证方法*/
    30. @Override
    31. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    32. /*根据token获取账号*/
    33. String username = (String) authenticationToken.getPrincipal();
    34. /*定义查询封装类,将查询的条件封装进去*/
    35. QueryWrapper queryWrapper = new QueryWrapper<>();
    36. queryWrapper.eq("username",username);
    37. queryWrapper.eq("is_deleted",0);
    38. /*根据账号查询用户信息*/
    39. User one = iUserService.getOne(queryWrapper);
    40. if (one != null) {
    41. /*获取盐*/
    42. ByteSource byteSource = ByteSource.Util.bytes(one.getSalt());
    43. /*从数据库获取密码*/
    44. SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(one, one.getPassword(), byteSource, this.getName());
    45. return info;
    46. }
    47. return null;
    48. }
    49. }

    添加自定义的过滤器

    1. import com.fasterxml.jackson.databind.ObjectMapper;
    2. import com.ytr.system.vo.CommonResult;
    3. import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
    4. import javax.servlet.ServletRequest;
    5. import javax.servlet.ServletResponse;
    6. import java.io.PrintWriter;
    7. /**
    8. * @author Yang
    9. * @ClassName Myfilter
    10. * @date 2022/8/7 10:41
    11. */
    12. public class Myfilter extends FormAuthenticationFilter {
    13. @Override
    14. //未登录时进入该方法
    15. protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
    16. response.setContentType("application/json;charset=utf-8");
    17. PrintWriter writer = response.getWriter();
    18. CommonResult commonResult = new CommonResult(3000, "未登录", null);
    19. ObjectMapper objectMapper = new ObjectMapper();
    20. String json = objectMapper.writeValueAsString(commonResult);
    21. writer.print(json);
    22. writer.flush();
    23. writer.close();
    24. return false;
    25. }
    26. }

    修改登录接口

    修改

    1. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    2. import com.ytr.system.entity.User;
    3. import com.ytr.system.service.IUserService;
    4. import com.ytr.system.vo.CommonResult;
    5. import com.ytr.system.vo.LoginVo;
    6. import io.swagger.annotations.Api;
    7. import io.swagger.annotations.ApiOperation;
    8. import org.apache.shiro.SecurityUtils;
    9. import org.apache.shiro.authc.UsernamePasswordToken;
    10. import org.apache.shiro.subject.Subject;
    11. import org.springframework.beans.factory.annotation.Autowired;
    12. import org.springframework.data.redis.core.RedisTemplate;
    13. import org.springframework.data.redis.core.ValueOperations;
    14. import org.springframework.web.bind.annotation.*;
    15. import java.util.UUID;
    16. import java.util.concurrent.TimeUnit;
    17. /**
    18. * @author Yang
    19. * @ClassName LoginCon
    20. * @date 2022/8/8 15:30
    21. */
    22. @RestController
    23. @RequestMapping(value = "/system")
    24. @Api(tags = "登录接口类")
    25. public class LoginCon {
    26. @Autowired
    27. IUserService userService;
    28. @Autowired
    29. private RedisTemplate redisTemplate;
    30. @ApiOperation(value = "登录接口方法")
    31. @PostMapping(value = "/login")
    32. public CommonResult login(@RequestBody LoginVo loginVo){
    33. try{
    34. /*获取subject*/
    35. Subject subject = SecurityUtils.getSubject();
    36. /*封装用户信息*/
    37. UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(loginVo.getUid(),loginVo.getUpwd());
    38. /*调用login方法*/
    39. subject.login(usernamePasswordToken);
    40. /*获取查询到的用户信息 */
    41. Object principal = subject.getPrincipal();
    42. //随机生成一个唯一字符串。
    43. String token = UUID.randomUUID().toString();
    44. //把该token作为redis的key value为当前登录用户信息
    45. ValueOperations forValue = redisTemplate.opsForValue();
    46. /*存值,并设置有效时间*/
    47. forValue.set(token,principal,24, TimeUnit.HOURS);
    48. return new CommonResult(2000,"登录成功",token);
    49. }catch (Exception e){
    50. return new CommonResult(5000,"登陆失败",null);
    51. }
    52. }
    53. }

    进行测试

    这个跨域问题之前已经解决,这篇笔记用的就是在下面的笔记代码中修改来的,在里面已经解决了跨域问题,可这里又出现了

     前后端分离https://blog.csdn.net/weixin_43766390/article/details/126228074?spm=1001.2014.3001.5501

    原因:

    前端的请求被shiro得拦截器给拦截了。前端VUE发送两种请求方式,OPTIONS和http请求,

    浏览器会首先发送预检请求OPTIONS请求,当OPTIONS请求通过之后,会再次发送我们当前的实际请求,所以我们要做的是让请求通过预检。

    解决方法

    修改我们写的过滤器添加一个登录判断

    1. @Override
    2. /*这个方法返回true的话下面的未登录方法一定不会执行 */
    3. protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
    4. HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    5. /*获取请求方式*/
    6. String method = httpServletRequest.getMethod();
    7. if(method!=null && method.equals("OPTIONS")){
    8. return true;
    9. }
    10. /*判断请求头是否携带token值*/
    11. String taken = httpServletRequest.getHeader("taken");
    12. if(taken!=null){
    13. return true;
    14. }
    15. return false;
    16. }

    在获取token后应该再和redis中的对比,看看是不是确实存在的token

    解决方案: 

     

    1. @Autowired
    2. private RedisTemplate redisTemplate;
    3. /*创建过滤器*/
    4. @Bean(value = "shiroFilter")
    5. public ShiroFilterFactoryBean filterFactoryBean() {
    6. ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
    7. factoryBean.setSecurityManager(securityManager());
    8. //设置拦截规则
    9. HashMap map = new HashMap<>();
    10. map.put("/system/login", "anon");
    11. map.put("/**", "authc");
    12. //放行Swagger2页面,需要放行这些
    13. map.put("/swagger-ui.html", "anon");
    14. map.put("/swagger/**", "anon");
    15. map.put("/webjars/**", "anon");
    16. map.put("/swagger-resources/**", "anon");
    17. map.put("/v2/**", "anon");
    18. map.put("/static/**", "anon");
    19. map.put("/doc.html", "anon");
    20. map.put("/swagger2/**", "anon");
    21. factoryBean.setFilterChainDefinitionMap(map);
    22. //设置自定义认证过滤器(过滤未登录的)
    23. HashMap filterMap = new HashMap();
    24. filterMap.put("authc", new Myfilter(redisTemplate));
    25. factoryBean.setFilters(filterMap);
    26. return factoryBean;
    27. }

    过滤器

    1. import com.fasterxml.jackson.databind.ObjectMapper;
    2. import com.ytr.system.vo.CommonResult;
    3. import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
    4. import org.springframework.data.redis.core.RedisTemplate;
    5. import javax.servlet.ServletRequest;
    6. import javax.servlet.ServletResponse;
    7. import javax.servlet.http.HttpServletRequest;
    8. import java.io.PrintWriter;
    9. /**
    10. * @author Yang
    11. * @ClassName Myfilter
    12. * @date 2022/8/7 10:41
    13. */
    14. public class Myfilter extends FormAuthenticationFilter {
    15. private RedisTemplate redisTemplate;
    16. /*定义构造函数*/
    17. public Myfilter(RedisTemplate redisTemplate){
    18. this.redisTemplate=redisTemplate;
    19. }
    20. @Override
    21. /*这个方法返回true的话下面的未登录方法一定不会执行 */
    22. protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
    23. HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    24. /*获取请求方式*/
    25. String method = httpServletRequest.getMethod();
    26. if(method!=null && method.equals("OPTIONS")){
    27. return true;
    28. }
    29. /*判断请求头是否携带token值*/
    30. String taken = httpServletRequest.getHeader("token");
    31. System.out.println(taken);
    32. if(taken!=null && redisTemplate.hasKey(taken)){
    33. System.out.println(taken);
    34. return true;
    35. }
    36. return false;
    37. }
    38. @Override
    39. //未登录时进入该方法
    40. protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
    41. response.setContentType("application/json;charset=utf-8");
    42. PrintWriter writer = response.getWriter();
    43. CommonResult commonResult = new CommonResult(3000, "未登录", null);
    44. ObjectMapper objectMapper = new ObjectMapper();
    45. String json = objectMapper.writeValueAsString(commonResult);
    46. writer.print(json);
    47. writer.flush();
    48. writer.close();
    49. return false;
    50. }
    51. }

    注意

    设置axios基础路径

    退出功能(清空前端session)

    前端代码

     后端

    1. /*退出*/
    2. @GetMapping("/quit")
    3. public CommonResult quit(HttpServletRequest request){
    4. /*从前端请求中获取token*/
    5. String token = request.getHeader("token");
    6. /*对比数据库查看有没有这个token*/
    7. if(redisTemplate.hasKey(token)){
    8. /*删除*/
    9. redisTemplate.delete(token);
    10. return new CommonResult(2000,"退出成功",null);
    11. }
    12. return new CommonResult(5000,"退出失败",null);
    13. }

    递归查询多级菜单

    1. import com.ytr.system.entity.Permission;
    2. import com.ytr.system.entity.User;
    3. import com.ytr.system.mapper.PermissionMapper;
    4. import com.ytr.system.service.IPermissionService;
    5. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    6. import com.ytr.system.vo.CommonResult;
    7. import org.springframework.beans.factory.annotation.Autowired;
    8. import org.springframework.data.redis.core.RedisTemplate;
    9. import org.springframework.data.redis.core.ValueOperations;
    10. import org.springframework.stereotype.Service;
    11. import java.util.ArrayList;
    12. import java.util.List;
    13. /**
    14. *

    15. * 权限 服务实现类
    16. *

    17. *
    18. * @author ytr
    19. * @since 2022-08-08
    20. */
    21. @Service
    22. public class PermissionServiceImpl extends ServiceImpl implements IPermissionService {
    23. @Autowired
    24. protected RedisTemplate redisTemplate;
    25. @Autowired
    26. protected PermissionMapper permissionMapper;
    27. @Override
    28. public CommonResult findPermissionByuserId(String token) {
    29. //根据token获取用户信息
    30. ValueOperations valueOperations = redisTemplate.opsForValue();
    31. User user = (User) valueOperations.get(token);
    32. String id = user.getId();
    33. /*根据用户id查询所具有的菜单*/
    34. List permissions = permissionMapper.selMenuByUserId(id);
    35. //设置层级关系
    36. List firstMenus = new ArrayList<>();
    37. for (Permission first : permissions) {
    38. //int
    39. if (first.getPid().equals("1")) {
    40. firstMenus.add(first);
    41. }
    42. }
    43. //为一级菜单设置二级菜单
    44. for (Permission first : firstMenus) {
    45. //根据一级菜单id 查询 该菜单得二级菜单。如果出现不确定有几级菜单 那么我们可以使用方法得递归调用
    46. first.setChildren(findChildren(permissions, first.getId()));
    47. }
    48. System.out.println("打印firstMenus"+firstMenus);
    49. return new CommonResult(2000,"查询成功",firstMenus);
    50. }
    51. //方法递归
    52. private List findChildren(List permissionList, String id) {
    53. List children = new ArrayList<>();
    54. for (Permission p : permissionList) {
    55. if (p.getPid().equals(id)) {
    56. children.add(p);
    57. }
    58. }
    59. for (Permission child : children) {
    60. child.setChildren(findChildren(permissionList, child.getId()));
    61. }
    62. return children;
    63. }
    64. }

  • 相关阅读:
    stm32使用通用定时器生成pwm
    1014 Waiting in Line(30)& 1017 Queueing at Bank(25)
    登陆页面/登陆框渗透测试思路
    MyBatis-Plus中解决表名或字段名不一致
    中小企业如何实施MES管理系统
    【重温C++ Primer】第一章、初识C++
    455-C++ 多态(知乎)
    【Vue3响应式原理#02】Proxy and Reflect
    C++这么难,为什么我们还要用C++?C++ Core Guidelines解析给了我答案
    【论文阅读】Hypergraph Convolutional Network for Group Recommendation
  • 原文地址:https://blog.csdn.net/weixin_43766390/article/details/126246595