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

一,springboot集成shiro

1,导入依赖

xml 复制代码
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.4.0</version>
        </dependency>

2,Realm

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

java 复制代码
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配置

java 复制代码
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:

java 复制代码
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重写里面的两个方法即可:

java 复制代码
org.apache.shiro.web.filter.authc.FormAuthenticationFilter#onAccessDenied
org.apache.shiro.web.filter.authc.FormAuthenticationFilter#onLoginSuccess
java 复制代码
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

java 复制代码
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);
    }

}
相关推荐
utmhikari23 分钟前
【架构艺术】Go语言微服务monorepo的代码架构设计
后端·微服务·架构·golang·monorepo
蜡笔小新星25 分钟前
Flask项目框架
开发语言·前端·经验分享·后端·python·学习·flask
计算机学姐29 分钟前
基于Asp.net的驾校管理系统
vue.js·后端·mysql·sqlserver·c#·asp.net·.netcore
欢乐少年19042 小时前
SpringBoot集成Sentry日志收集-3 (Spring Boot集成)
spring boot·后端·sentry
夏天的味道٥3 小时前
使用 Java 执行 SQL 语句和存储过程
java·开发语言·sql
冰糖码奇朵5 小时前
大数据表高效导入导出解决方案,mysql数据库LOAD DATA命令和INTO OUTFILE命令详解
java·数据库·sql·mysql
好教员好5 小时前
【Spring】整合【SpringMVC】
java·spring
浪九天6 小时前
Java直通车系列13【Spring MVC】(Spring MVC常用注解)
java·后端·spring
堕落年代7 小时前
Maven匹配机制和仓库库设置
java·maven
功德+n7 小时前
Maven 使用指南:基础 + 进阶 + 高级用法
java·开发语言·maven