当前位置: 首页 > article >正文

springboot集成shiro和前后端分离配置

一,springboot集成shiro

1,导入依赖

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.4.0</version>
        </dependency>

2,Realm

shiro以来这个来进行认证和授权

package com.chen.admin.Realm;

import com.alibaba.fastjson.JSON;
import com.chen.admin.dto.UserDto;
import com.chen.admin.entity.Role;
import com.chen.admin.entity.User;
import com.chen.admin.service.RoleService;
import com.chen.admin.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @Author @Chenxc
 * @Date 24-7-2 10:13
 */
@Component
public class UserPasswordRealm extends AuthorizingRealm {

    @Autowired
    private RoleService roleService;

    @Autowired
    private UserService userService;

    @Autowired
    private PasswordEncoder encoder;

   // @Autowired
    //private StringRedisTemplate stringRedisTemplate;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        UserDto user = (UserDto) principalCollection.getPrimaryPrincipal();

        //使用redis存放用户角色和权限
//        Object o = stringRedisTemplate.opsForValue().get("user:" + user.getId());
//        if(null == o){
//            List<Role> roleList = roleService.getRoleByUserId(user.getId());
//            if(null != roleList){
//                Set<String> roles = new HashSet<>();
//                Set<String> permissions = new HashSet<>();
//                for (Role role : roleList) {
//                    roles.add(role.getName());
//                    List<String> permission = roleService.getPermissionByRoleId(role.getId());
//                    if(null != permission){
//                        permissions.addAll(permission);
//                    }
//                }
//                user.setRoleList(roles);
//                user.setPermissions(permissions);
//            }
//            stringRedisTemplate.opsForValue().set("user:"+user.getId(), JSON.toJSONString(user));
//        }else{
//            String json = (String)o;
//            user = JSON.parseObject(json,UserDto.class);
//        }
//        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//        simpleAuthorizationInfo.addRoles(user.getRoleList());
//        simpleAuthorizationInfo.addStringPermissions(user.getPermissions());

	 //本地存放用户角色和权限
        List<Role> roleList = roleService.getRoleByUserId(user.getId());
        if(null != roleList){
            Set<String> roles = new HashSet<>();
            Set<String> permissions = new HashSet<>();
            for (Role role : roleList) {
                roles.add(role.getName());
                List<String> permission = roleService.getPermissionByRoleId(role.getId());
                if (null != permission) {
                    permissions.addAll(permission);
                }
            }
            user.setRoleList(roles);
            user.setPermissions(permissions);
        }
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addRoles(user.getRoleList());
        simpleAuthorizationInfo.addStringPermissions(user.getPermissions());
        return simpleAuthorizationInfo;
    }


    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        char[] password1 = token.getPassword();

        if(null == username || null  == password1){
            throw new AuthenticationException("用户名或密码错误");
        }

        String password = new String(password1);
        User user = userService.getUserByUsername(username);
        if(null == user){
            throw new AuthenticationException("用户名或密码错误");
        }
        boolean matches = encoder.matches(password, user.getPassword());
        if (!matches) {
            throw new AuthenticationException("用户名或密码错误");
        }

        if(user.getEnable().equals("0")){
            throw new DisabledAccountException("用户已禁用");
        }

        UserDto dto = new UserDto();
        BeanUtils.copyProperties(user,dto);
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(dto, password, getName());
        return authenticationInfo;
    }
}

3,shiro配置

package com.chen.admin.config;


import com.chen.admin.dao.RedisSessionDao;
import com.chen.admin.filter.ShiroFormAuthenticationFilter;
import com.chen.admin.system.Constant;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Author @Chenxc
 * @Date 24-7-2 10:10
 */
@Configuration
public class ShiroConfig {

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        // 设置为true则会在代理对象的方法执行过程中进行权限校验
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    //上面的两个开启注解的支持,如果没有这两个方法,doGetAuthorizationInfo不会执行,加了@RequiresPermissions注解后会包找不到url


    //session保存在内存
    @Bean
    public DefaultWebSessionManager defaultWebSessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(Constant.expireTime * 1000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        //修改Cookie中的SessionId的key,默认为JSESSIONID,自定义名称
        sessionManager.setSessionIdCookie(new SimpleCookie(Constant.SHIRO_COOKIE_ID));
        return sessionManager;
    }


    //session保存在redis
    @Bean
    public DefaultWebSessionManager defaultWebSessionManager(RedisSessionDao redisSessionDao){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(Constant.expireTime * 1000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionDAO(redisSessionDao);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        //修改Cookie中的SessionId的key,默认为JSESSIONID,自定义名称
        sessionManager.setSessionIdCookie(new SimpleCookie(Constant.SHIRO_COOKIE_ID));
        return sessionManager;
    }

//上面的session存放选一个即可

    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(DefaultWebSessionManager defaultWebSessionManager,Realm realm){
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        // 取消Cookie中的RememberMe参数
        manager.setRememberMeManager(null);
        manager.setRealm(realm);
        manager.setSessionManager(defaultWebSessionManager);
        return manager;
    }



    @Bean
    public ShiroFilterFactoryBean shiroFilterChainDefinition(DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(defaultWebSecurityManager);
        factoryBean.setLoginUrl("/login");
        //factoryBean.setSuccessUrl("/loginSuccess");
        Map<String, String> map = new LinkedHashMap<>();
        //map.put("/login","anon");
        map.put("/static/**","anon");
        map.put("/ws/**","authc");
        map.put("/**", "authc");

        // 配置shiro默认登录界面地址,前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据
//        shiroFilterFactoryBean.setLoginUrl("/login/unauth");
        LinkedHashMap<String, Filter> filtsMap = new LinkedHashMap<>();
        // 这里使用自定义的filter
        filtsMap.put("authc", new ShiroFormAuthenticationFilter());
        factoryBean.setFilters(filtsMap);
        factoryBean.setFilterChainDefinitionMap(map);
        return factoryBean;
    }

}

如果上面使用redis还需要集成 AbstractSessionDAO 来读取redis中的数据来认证和授权

RedisSessionDao:

package com.chen.admin.dao;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chen.admin.system.Constant;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.util.JSONPObject;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Collection;
import java.util.concurrent.TimeUnit;

/**shiro集成redis
 * @author @Chenxc
 * @date 2024/7/3 0:46
 **/

@Component
public class RedisSessionDao extends AbstractSessionDAO {


    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    protected Serializable doCreate(Session session) {
        Serializable serializable = this.generateSessionId(session);
        this.assignSessionId(session,serializable);
        redisTemplate.opsForValue().set(session.getId(),session, Constant.expireTime, TimeUnit.SECONDS);
        return serializable;
    }

    @Override
    protected Session doReadSession(Serializable serializable) {
        if(serializable == null){
            return null;
        }
        SimpleSession o = (SimpleSession)redisTemplate.opsForValue().get(serializable);
        if(o == null){
            return null;
        }
        return o;
    }

    @Override
    public void update(Session session) throws UnknownSessionException {
        if(session != null && session.getId() != null){
            session.setTimeout( Constant.expireTime * 1000);
            redisTemplate.opsForValue().set(session.getId(),session, Constant.expireTime,TimeUnit.SECONDS);
        }
    }

    @Override
    public void delete(Session session) {
        if (session != null && session.getId() != null) {
            redisTemplate.opsForValue().getOperations().delete(session.getId());
        }
    }

    @Override
    public Collection<Session> getActiveSessions() {
        return redisTemplate.keys("*");
    }
}

二,前后端分离配置

如果是前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据,可以继承FormAuthenticationFilter重写里面的两个方法即可:

org.apache.shiro.web.filter.authc.FormAuthenticationFilter#onAccessDenied
org.apache.shiro.web.filter.authc.FormAuthenticationFilter#onLoginSuccess
package com.chen.admin.filter;

import com.alibaba.fastjson.JSON;
import com.chen.admin.system.Result;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**

 * @author @Chenxc
 * @date 2024/7/8 1:29
 **/
public class ShiroFormAuthenticationFilter extends FormAuthenticationFilter {
    private static final Logger log = LoggerFactory.getLogger(ShiroFormAuthenticationFilter.class);

    /** 未登录时返回json
     * @auther: @Chenxc		//作者
     * @Description //TODO 	//描述
     * @param: 	//参数
     * @return: 	//返回值
     * @date:  	//创建日期
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (this.isLoginRequest(request, response)) {
            if (this.isLoginSubmission(request, response)) {
                if (log.isTraceEnabled()) {
                    log.trace("Login submission detected.  Attempting to execute login.");
                }

                return this.executeLogin(request, response);
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("Login page view.");
                }

                return true;
            }
        } else {
            if (log.isTraceEnabled()) {
                log.trace("Attempting to access a path which requires authentication.  Forwarding to the Authentication url [" + this.getLoginUrl() + "]");
            }

            //this.saveRequestAndRedirectToLogin(request, response);
            response.setContentType("application/json; charset=utf-8");
            response.setCharacterEncoding("UTF-8");
            PrintWriter out = response.getWriter();
            out.println(JSON.toJSONString(Result.unauthenticated()));
            out.flush();
            out.close();
            return false;
        }
    }


    /** 登录成功后返回json
     * @auther: @Chenxc		//作者
     * @Description //TODO 	//描述
     * @param: 	//参数
     * @return: 	//返回值
     * @date:  	//创建日期
     */
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        if (log.isTraceEnabled()) {
            log.trace("Attempting to access a path which requires authentication.  Forwarding to the Authentication url [" + this.getLoginUrl() + "]");
        }

        //this.saveRequestAndRedirectToLogin(request, response);
        response.setContentType("application/json; charset=utf-8");
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        Result result = Result.success("登录成功");
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        String header = httpServletResponse.getHeader("Set-Cookie");
        if(null != header){
            String[] split = header.split(";");
            if(null != split && split.length != 0){
                String tokenStr = split[0].split("=")[1];
                result.setData(tokenStr);
            }
        }
        out.println(JSON.toJSONString(result));
        out.flush();
        out.close();
        return false;
    }
}

最后controller

package com.chen.admin.controller;

import com.chen.admin.entity.Menu;
import com.chen.admin.service.MenuService;
import com.chen.admin.system.Result;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author @Chenxc
 * @date 2024/7/15 23:46
 **/
@RestController
@RequestMapping("/menu")
@RequiresPermissions({"MENU"})//访问该controller需要的权限
public class MenuController {

    @Autowired
    private MenuService menuService;


    @RequestMapping("/list")
    public Result<Menu> list(){
        return menuService.menuList();
    }


    @RequestMapping("/save")
    public Result<Menu> save(Menu menu){
        return menuService.saveMenu(menu);
    }


    @RequestMapping("/delete")
    public Result delete(Long id){
        return menuService.delete(id);
    }

    @RequestMapping("/update")
    public Result update(Menu menu){
        return menuService.updateMenu(menu);
    }

}


http://www.kler.cn/a/409466.html

相关文章:

  • springboot集成shiro和前后端分离配置
  • IT服务团队建设与管理
  • 【计算机网络】多路转接之poll
  • C++中定义类型名的方法
  • Linux网络——网络层
  • 快速获取镜像包的方法
  • matlab 反距离插值 IDW
  • 【系统架构设计师】真题论文: 论非功能性需求对企业应用架构设计的影响(包括解题思路和素材)
  • 基于YOLOv8深度学习的智慧交通事故检测系统研究与实现(PyQt5界面+数据集+训练代码)
  • jdk8特性:CompletableFuture的使用
  • 小R的随机播放顺序
  • 论文 | Recitation-Augmented Language Models
  • 6.STM32之通信接口《精讲》之USART通信(PC串口与OLED交互)---多字节数据收发(数据包的模式:HEX数据包和文本数据包)
  • 五天SpringCloud计划——DAY1之mybatis-plus的使用
  • SpringBoot3中的性能提升介绍
  • Linux上安装单机版ElasticSearch6.8.1
  • 设计模式之创建模式篇
  • 签到送金币项目
  • 集合Queue、Deque、LinkedList、ArrayDeque、PriorityQueue详解
  • FastAPI和SQLModel结合的优点
  • 拉格朗日乘子(Lagrange Multiplier)是数学分析中用于解决带有约束条件的优化问题的一种重要方法,特别是SVM
  • selenium grid 远程webdriver添加上网代理
  • 7-401 平均值
  • Git 提交的相对引用
  • 【Linux】安装cuda
  • 实验7 JavaScript编程基础7.1密码验证