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);
    }

}
相关推荐
jakeswang9 分钟前
spring循环依赖以及MyBatis-Plus的继承特性导致循环依赖自动解决失效
java·spring·mybatis
疯一样的码农12 分钟前
使用命令行创建一个简单的 Maven Web 应用程序
java·maven
SlothLu28 分钟前
Debezium-BinaryLogClient
java·mysql·kafka·binlog·多线程·debezium·数据迁移
人才程序员34 分钟前
详解Qt QStorageInfo 存储信息类
c语言·开发语言·c++·后端·qt·界面
小奏技术1 小时前
聊聊HTTP2中的GOAWAY帧以及RocketMQ对GOAWAY的实现
后端·网络协议
Code_Artist1 小时前
细说Linux操作系统的网络I/O模型
linux·后端·网络协议
MZWeiei2 小时前
实现List接口的三类-ArrayList -Vector -LinkedList
java
怀旧6662 小时前
Java List 集合
java·数据结构·后端·list·个人开发
Peter_chq2 小时前
【计算机网络】多路转接之poll
linux·c语言·开发语言·网络·c++·后端·poll
AskHarries2 小时前
异步校验工具 awaitility 快速入门
后端