springboot集成shiro,使用token登陆,使用redis缓存

1.准备用户数据,这里我将数据写死存储;

public class Constant implements Serializable {
    public static final String USERNAME = "gaoyang";
    public static final String PASSWORD = "123456";
    public static final String ROLE1 = "admin";
    public static final String ROLE2 = "guest";

    public static  Map map;
    public static Map get(){
        if(map==null){
            map = new HashedMap();
        }
        return map;
    }
}

2.配置redis,这里value序列化使用的是jdk自己的,使用json会报错

spring:
  redis:
    host: 192.168.32.128
    port: 6382
    password: 123456
    jedis:
      pool:
        max-wait: 1
        max-idle: 10
        max-active: 8
        min-idle: 5
    timeout: 5000
@EnableCaching
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer();
        template.setValueSerializer(serializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(factory);
        return stringRedisTemplate;
    }
}

3.重写sessionid接受方式,这里我们接收前台传过来的token

public class SessionConfig extends DefaultWebSessionManager {
    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        String id = WebUtils.toHttp(request).getHeader("token");
        
        if (!StringUtils.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "Stateless request");
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
            //否则按默认规则从cookie取sessionId
            return super.getSessionId(request, response);
        }
    }
}

4.重写生成,读取,变更token信息的方法,使用redis保存

@Component
public class SessionDaoConfig extends EnterpriseCacheSessionDAO {

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = this.generateSessionId(session);
        SimpleSession session1 = (SimpleSession) session;
        session1.setId(sessionId);
        return sessionId;
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        Session o = (Session) redisTemplate.opsForValue().get(sessionId);
        return o;
    }

    @Override
    protected void doUpdate(Session session) {
        if (session instanceof ValidatingSession) {
            System.out.println(session.getTimeout());
            System.out.println(session.getId());
            if (((ValidatingSession) session).isValid()) {
                redisTemplate.opsForValue().set(session.getId(), session);
            } else {
                redisTemplate.delete(session.getId());
            }
        } else {
            redisTemplate.opsForValue().set(session.getId(), session);
        }
    }

    @Override
    protected void doDelete(Session session) {
        redisTemplate.delete(session.getId());
    }
}

5.自定义realm验证方法

@Component("myRealm")
public class MyRealm extends AuthorizingRealm {
    @Override
    public String getName() {
        return "myRealm";
    }

    //权限认证,通过名字读取角色死数据
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String username = (String) principalCollection.getPrimaryPrincipal();
        System.out.println(username);
        SimpleAuthorizationInfo sim = new SimpleAuthorizationInfo();
        if (username.equals("gaoyang")) {
            sim.addRole(Constant.ROLE1);
        } else {
            sim.addRole(Constant.ROLE2);
        }
        return sim;
    }

    //身份验证,这里密码写死
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String username = (String) authenticationToken.getPrincipal();
        if (username == null) {
            return null;
        }
        SimpleAuthenticationInfo sim = new SimpleAuthenticationInfo(username, Constant.PASSWORD, this.getName());
        return sim;
    }
    /*@Override
    public CredentialsMatcher getCredentialsMatcher() {
        HashedCredentialsMatcher hash = new HashedCredentialsMatcher();
        hash.setHashAlgorithmName("md5");
        hash.setHashIterations(2);
        return hash;
    }*/
}

6.设置shiro权限控制中心

@Configuration
public class ShiroConfig   {
    @Autowired
    private MyRealm myRealm;

    @Bean("shirFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") SecurityManager securityManager) {
        ShiroFilterFactoryBean shiro = new ShiroFilterFactoryBean();
        shiro.setLoginUrl("/unlogin");  //没有登陆的json返回
        shiro.setUnauthorizedUrl("/no");    //没有权限的json返回
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/ajaxLogin", "anon");
        filterChainDefinitionMap.put("/map", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        //注意,如果roles[admin,guest]是用户需要同时包含两者角色才可以访问,是且的关系;
        //如果想改为或的关系,请继承AuthorizationFilter并加入过滤连,perm资源也是一样,需要继承PermissionsAuthorizationFilter加入过滤链;
        filterChainDefinitionMap.put("/test", "authc,roles[admin]");
        filterChainDefinitionMap.put("/**", "authc");
        shiro.setFilterChainDefinitionMap(filterChainDefinitionMap);
        shiro.setSecurityManager(securityManager);
        return shiro;
    }


    @Bean("securityManager")
    public SecurityManager securityManager(SessionDaoConfig sessionDaoConfig) {
        DefaultWebSecurityManager def = new DefaultWebSecurityManager();
        def.setRealm(myRealm);
        // 自定义session管理 使用redis
        SessionConfig sessionConfig = new SessionConfig();
        sessionConfig.setSessionDAO(sessionDaoConfig);
        //sessionConfig.setSessionDAO(new SessionDaoConfig());
        def.setSessionManager(sessionConfig);
        // 自定义缓存实现 使用redis
        //def.setCacheManager();
        return def;
    }
}

7.controller层的登陆,退出等

@RestController
public class IndexController {

    @Autowired
    private RedisTemplate<Object,Object>redisTemplate;

    @CrossOrigin
    @GetMapping("/login")
    public Object login(String username, String password) {
        Map<String, Object> map = new HashMap<>();
        map.put("code", 200);
        map.put("msg", "登录成功");
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        Subject subject = SecurityUtils.getSubject();
        map.put("token", subject.getSession().getId());
        try {
            subject.login(token);
            Session session = subject.getSession();
            //这里设置session过期时间
            session.setTimeout(25000);
            session.setAttribute("currentuser", username);
        } catch (Exception e) {
            map.put("code", 204);
            map.put("msg", "登录失败");
            return map;
        }
        return map;
    }

    @GetMapping("/unlogin")
    public Object unlogin() {
        Map<String, Object> map = new HashMap<>();
        map.put("code", 204);
        map.put("msg", "未登录");
        return map;
    }

    @GetMapping("/test")
    public Object test() {
        Map<String, Object> map = new HashMap<>();
        Subject subject = SecurityUtils.getSubject();
        Object currentuser = subject.getSession().getAttribute("currentuser");
        map.put("code", 200);
        map.put("msg", "查询成功");
        map.put("data", currentuser);
        return map;
    }

    @GetMapping("/logout")
    public Object logout() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        Map<String, Object> map = new HashMap<>();
        map.put("code", 200);
        map.put("msg", "退出成功");
        return map;
    }

8.自定义的过滤替代roles和perms

public class PermConfig extends PermissionsAuthorizationFilter {
    @Override
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
        Subject subject = this.getSubject(request, response);
        String[] rolesArray = (String[]) mappedValue;
        if (rolesArray == null || rolesArray.length == 0) {
            //no roles specified, so nothing to check - allow access.
            return true;
        }
        for (int i = 0; i < rolesArray.length; i++) {
            if (subject.isPermitted(rolesArray[i])) {
                return true;
            }
        }
        return false;
    }
}
public class RoleConfig extends AuthorizationFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
        Subject subject = this.getSubject(servletRequest, servletResponse);
        String[] rolesArray = (String[]) o;

        if (rolesArray == null || rolesArray.length == 0) {
            //no roles specified, so nothing to check - allow access.
            return true;
        }
        for (int i = 0; i < rolesArray.length; i++) {
            if (subject.hasRole(rolesArray[i])) {
                return true;
            }
        }
        return false;
    }
}

不过作为前后端分离项目,用户的信息及过期权限等信息依然是靠后端存储,以上依然涉及session,只不过是将产生的jsessionid当作token使用,使用redis存储而已.可以考虑使用jwt,彻底是后端无状态化;

  • 4
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
Spring Boot是一个开源的Java框架,用于构建独立的、可执行的、生产级的Spring应用程序。它极大地简化了Spring应用程序的搭建和部署过程,提供了一整套开箱即用的特性和插件,极大地提高了开发效率。 Shiro是一个强大且灵活的开源Java安全框架,提供了身份验证、授权、加密和会话管理等功能,用于保护应用程序的安全。它采用插件化的设计,支持与Spring等常用框架的无缝集成,使开发者能够轻松地在应用程序中添加安全功能。 JWT(JSON Web Token)是一种用于在客户端和服务端之间传输安全信息的开放标准。它使用JSON格式对信息进行包装,并使用数字签名进行验证,确保信息的完整性和安全性。JWT具有无状态性、可扩展性和灵活性的特点,适用于多种应用场景,例如身份验证和授权。 Redis是一个开源的、高性能的、支持多种数据结构的内存数据库,同时也可以持久化到磁盘中。它主要用于缓存、消息队列、会话管理等场景,为应用程序提供高速、可靠的数据访问服务。Redis支持丰富的数据类型,并提供了强大的操作命令,使开发者能够灵活地处理各种数据需求。 综上所述,Spring Boot结合Shiro、JWT和Redis可以构建一个安全、高性能的Java应用程序。Shiro提供了强大的安全功能,包括身份验证和授权,保护应用程序的安全;JWT用于安全传输信息,确保信息的完整性和安全性;Redis作为缓存和持久化数据库,提供了高速、可靠的数据访问服务。通过使用这些技术,开发者能够快速、高效地构建出符合安全和性能需求的应用程序。
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值