搭建springboot + shiro 项目

这次我们搭建一个springboot + mybatis + shiro + mysql + redis + thymeleaf + hutool + layui项目。期望能够实现这样的效果:

  1. 能对登录及其他请求做到控制
  2. 对后端接口能做权限控制
  3. 对前台页面能做权限控制
  4. 实现session共享

数据库表准备

在这里我们准备3张表,分别为

  • 用户表,表结构和数据如下:

图片

  • 角色表,表结构和数据如下:

图片

  • 权限表,表结构和数据如下:

图片

实现登录

后台

创建一个springboot项目

可以参考文章《搭建spring boot + mybatis项目》,我们这里在之前搭建好的springboot项目的基础上进行。

自动生成代码

生成对应上面3张表的dao层,实体类等。编写对应的service层,dao层及*Mapper.xml层等。

能够实现需要的查询就好,这里不再赘述。

集成shiro

添加依赖

这里需要添加shiro依赖shiro-spring;

模板引擎我们用springboot推荐的thymeleaf,添加依赖spring-boot-starter-thymeleaf;

为了瘦身一些实体类(不写get/set方法),添加依赖lombok;

在登录时的验证码图片,我们用hutool提供的工具类完成,添加依赖hutool-all;

<!--Apache Shiro依赖包-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
</dependency>
<!--引入thymeleaf-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>4.6.10</version>
</dependency>
添加shiro配置类

在config包下,新建一个ShiroConfig配置类,代码如下:

@Configuration
public class ShiroConfig {
    @Resource
    private ShiroUserService shiroUserService;
    @Resource
    private ShiroRoleMapper shiroRoleMapper;
    // shiro的过滤器链
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/toLogin");   // 登录页面,请求被拦截后会重定向到登录页面
//        shiroFilterFactoryBean.setSuccessUrl("/index");   // 这个是附件配置,一般情况不生效,原因看源码
//        shiroFilterFactoryBean.setUnauthorizedUrl("/notRole");  // 跳转到没有权限的页面
        // 过滤器链,从上到下,按顺序优先适配
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/logout", "logout");  // shiro内部已经实现了logout
        filterChainDefinitionMap.put("/captcha_img", "anon");
        //静态文件不拦截
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/img/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/layui/**", "anon");
        //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    //  shiro的安全管理中心
    @Bean
    public SecurityManager securityManager(AuthorizingRealm customizeRealm) {
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        defaultSecurityManager.setRealm(customizeRealm);
        return defaultSecurityManager;
    }
    // 自定义域
    @Bean
    public AuthorizingRealm customizeRealm(){
        AuthorizingRealm customizeRealm = new AuthorizingRealm() {
            // 授权处理
            @Override
            protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
                return null;
            }
            // 认证处理
            @Override
            protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
                String userName = (String) token.getPrincipal();
                String userPwd = new String((char[]) token.getCredentials());
                //根据用户名从数据库查询用户
                ShiroUser user = shiroUserService.findUserByUserName(userName);
                if (user == null) {
                    throw new AccountException("用户名不正确");
                } else if (!userPwd.equals(user.getPassword().toString())) {
                    throw new AccountException("密码不正确");
                }
                return new SimpleAuthenticationInfo(userName, user.getPassword().toString(), getName());
            }
        };
        return customizeRealm;
    }
}
登录接口

这里我们新建一个AccountController类,来处理登录相关操作。一共需要4个接口:登录页面页面接口;登录处理接口;展示首页接口。第四个是在登录时,利用hutool生成的一个验证码,可以不写,登录页面就没有验证码了。

@Controller
@Slf4j
public class AccountController {
    // 登录页面
    @RequestMapping(value = "/toLogin")
    public String defaultLogin() {
        return "login";
    }
    // 登录处理
    @PostMapping(value = "/login")
    public String login(@RequestParam("username") String username, @RequestParam("password") String password) throws UnknownAccountException, IncorrectCredentialsException, LockedAccountException {
        // 从SecurityUtils里边templates创建一个 subject
        Subject subject = SecurityUtils.getSubject();
        if (subject.isAuthenticated()) {
            return "redirect:index";
        }
        // 在认证提交前准备 token(令牌)
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        // 执行认证登陆
        try {
            // 登录认证成功会自动重定向到"/index"
            subject.login(token);
            return "redirect:index";
        } catch (ExcessiveAttemptsException eae) {
            log.error("用户名或密码错误次数过多");
        } catch (AuthenticationException ae) {
            log.error("用户名或密码不正确");
        }
        return "login";
    }
    // 首页
    @GetMapping("/index")
    public String index(){
        return "index";
    }
    // 利用hutool,生成验证码,登录时用
    @GetMapping("/captcha_img")
    public void create(HttpServletResponse response) throws IOException {
        //定义图形验证码的长、宽、验证码字符数、干扰线宽度
        CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(116, 36, 4, 5);
        //得到code
        String code = captcha.getCode();
        System.out.println(code);
        //放到session
//        session.setAttribute("code", code);
        ServletOutputStream outputStream = response.getOutputStream();
        captcha.write(outputStream);
        outputStream.close();
    }

前端

下面这些页面,可以直接从layui官方文档上复制下来稍改下来用,网址:https://www.layui.com/doc/element/layout.html

登录页面

在resource/template/下新创建一个login.html页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <base th:href="${#request.getContextPath()}+'/'">
  <title>登录</title>
  <link rel="stylesheet" href="layui/css/layui.css">
</head>
<body>
<form class="layui-form" action="login" method="post">
  <div class="layui-form-item">
    <label class="layui-form-label">用户名</label>
    <div class="layui-input-block">
      <input type="text" name="username" value="test" required  lay-verify="required" placeholder="请输入用户名" autocomplete="off" class="layui-input">
    </div>
  </div>
  <div class="layui-form-item">
    <label class="layui-form-label">密码框</label>
    <div class="layui-input-inline">
      <input type="password" name="password" value="123456" required lay-verify="required" placeholder="请输入密码" autocomplete="off"  class="layui-input">
    </div>
    <div class="layui-form-mid layui-word-aux"><img src="captcha_img" onclick="changeImg(this)"></div>
  </div>
  <div class="layui-form-item">
    <div class="layui-input-block">
      <button class="layui-btn" lay-submit lay-filter="formDemo">登录</button>
      <button type="reset" class="layui-btn layui-btn-primary">重置</button>
    </div>
  </div>
</form>
<script src="layui/layui.js"></script>
<script>
//一般直接写在一个js文件中
layui.use(['layer', 'form'], function(){
  var layer = layui.layer
  ,form = layui.form;
  layer.msg('Hello World');
});
</script>
<script>
  // 点击验证码时,生成新的验证码
  function changeImg(obj) {
      obj.src = "captcha_img?count=1&timestamp=" + new Date().getTime();
  }
</script>
</body>
</html>

首页

在resource/template/下新创建一个index.html页面

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>index</title>
    <link rel="stylesheet" href="layui/css/layui.css">
</head>
<body class="layui-layout-body">
<div class="layui-layout layui-layout-admin">
    <div class="layui-header">
        <div class="layui-logo">layui 后台布局</div>
        <!-- 头部区域(可配合layui已有的水平导航) -->
        <ul class="layui-nav layui-layout-left">
            <li class="layui-nav-item"><a href="/role/list">角色</a></li>
            <li class="layui-nav-item"><a href="">权限</a></li>
            <li class="layui-nav-item"><a href="">用户</a></li>
            <li class="layui-nav-item">
                <a href="javascript:;">其它系统</a>
                <dl class="layui-nav-child">
                    <dd><a href="">邮件管理</a></dd>
                    <dd><a href="">消息管理</a></dd>
                    <dd><a href="">授权管理</a></dd>
                </dl>
            </li>
        </ul>
        <ul class="layui-nav layui-layout-right">
            <li class="layui-nav-item">
                <a href="javascript:;">
                    <img src="http://t.cn/RCzsdCq" class="layui-nav-img">
                    贤心
                </a>
                <dl class="layui-nav-child">
                    <dd><a href="">基本资料</a></dd>
                    <dd><a href="">安全设置</a></dd>
                </dl>
            </li>
            <li class="layui-nav-item"><a href="logout">退了</a></li>
        </ul>
    </div>
    <div class="layui-side layui-bg-black">
        <div class="layui-side-scroll">
            <!-- 左侧导航区域(可配合layui已有的垂直导航) -->
            <ul class="layui-nav layui-nav-tree"  lay-filter="test">
                <li class="layui-nav-item layui-nav-itemed">
                    <a class="" href="javascript:;">所有商品</a>
                    <dl class="layui-nav-child">
                        <dd><a href="javascript:;">列表一</a></dd>
                        <dd><a href="javascript:;">列表二</a></dd>
                        <dd><a href="javascript:;">列表三</a></dd>
                        <dd><a href="">超链接</a></dd>
                    </dl>
                </li>
                <li class="layui-nav-item">
                    <a href="javascript:;">解决方案</a>
                    <dl class="layui-nav-child">
                        <dd><a href="javascript:;">列表一</a></dd>
                        <dd><a href="javascript:;">列表二</a></dd>
                        <dd><a href="">超链接</a></dd>
                    </dl>
                </li>
                <li class="layui-nav-item"><a href="">云市场</a></li>
                <li class="layui-nav-item"><a href="">发布商品</a></li>
            </ul>
        </div>
    </div>
    <div class="layui-body">
        <!-- 内容主体区域 -->
        <div style="padding: 15px;">内容主体区域</div>
    </div>
    <div class="layui-footer">
        <!-- 底部固定区域 -->
        © layui.com - 底部固定区域
    </div>
</div>
<script src="layui/layui.js"></script>
<script>
    //JavaScript代码区域
    layui.use('element', function(){
        var element = layui.element;
    });
</script>
</body>
</html>

测试

启动项目,输入:http://localhost:8080/ ,展示页面如下:

图片

接着点击登录按钮,显示页面如下:

图片

对后台接口加权限控制

后台

修改shiro配置类

在ShiroConfig#customizeRealm()方法中,给当前登录对象授权,在重新自定义域的授权方法,代码如下:

// 授权处理
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    Set<String> stringSet = new HashSet<>();
    String username = (String) SecurityUtils.getSubject().getPrincipal();
    ShiroUser user = shiroUserService.findUserByUserName(username);
    // 把用户的角色都存到权限里(这里只是为了做演示)
    List<ShiroRole> roleList = shiroRoleMapper.list(user.getId());
    roleList.forEach(r -> stringSet.add("role:" + r.getRoleName()));
    info.setStringPermissions(stringSet);
    return info;
}

在ShiroConfig要给后台接口加权限控制,需要开启spring aop功能,代码如下:

// 实现spirng的自动代理
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
    DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
    advisorAutoProxyCreator.setProxyTargetClass(true);
    return advisorAutoProxyCreator;
}
// 开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持;
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    return authorizationAttributeSourceAdvisor;
}

添加RoleController

添加一个RoleControler类,里面写个"/list"接口进行测试,如下:

@Controller
public class RoleController {
    
    @GetMapping("/list")
    @RequiresPermissions("role:admin")   // 有role:admin权限的可以访问
//    @RequiresPermissions("role:visitor")  // 有role:visitor权限的可以访问
    public String list(){
        return "role/list";
    }
}

前端

编写一个resource/template/role/list.html 页面,如果权限够,请可以访问到上面的“list”接口,展示到这个页面。代码如下:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <base th:href="${#request.getContextPath()}+'/'">
    <title>角色列表</title>
    <link rel="stylesheet" href="layui/css/layui.css">
</head>
<body>
<table class="layui-table">
    <colgroup>
        <col width="150">
        <col width="200">
        <col>
    </colgroup>
    <thead>
    <tr>
        <th>昵称</th>
        <th>加入时间</th>
        <th>签名</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>贤心</td>
        <td>2016-11-29</td>
        <td>人生就像是一场修行</td>
    </tr>
    <tr>
        <td>许闲心</td>
        <td>2016-11-28</td>
        <td>于千万人之中遇见你所遇见的人,于千万年之中,时间的无涯的荒野里…</td>
    </tr>
    </tbody>
</table>
<script src="layui/layui.js"></script>
<script>
    //一般直接写在一个js文件中
    layui.use(['layer', 'form'], function(){
        var layer = layui.layer
            ,form = layui.form;
        layer.msg('Hello World');
    });
</script>
</body>
</html>

测试

项目重启成功并且登录成功后,去访问 http://localhost:8080/list ,响应页面如下:

图片

现在RoleController#list() 方法上的@RequiresPermissions(“role:admin”) 注解注释上,@RequiresPermissions(“role:visitor”) 注解的注释去掉,重启项目登录成功后,去访问

http://localhost:8080/list,页面如下:

图片

看上面红色框中的报错信息,当前主体对象(当前登录用户)没有“role:visitor"权限。

对前端页面加权限控制

后台

pom中添加依赖

<!--thymeleaf和shiro整合包-->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>
<!--thymeleaf转换HTML网页格式包 加完之后能够识别thymeleaf以外的扩展标签-->
<dependency>
    <groupId>net.sourceforge.nekohtml</groupId>
    <artifactId>nekohtml</artifactId>
    <version>1.9.22</version>
</dependency>

application.yml中添加下面的配置

spring
  thymeleaf:
    mode: LEGACYHTML5 #支持网页格式为HTML5

shiro配置类ShiorConfig中添加下面配置

// 使得shiro标签能在前端页面使用
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect(){
    return new ShiroDialect();
}

前端

我们以index.html测试,在该页面中引入下面的标签库

xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"

在index.html页面的body添加两个标签,如下:
图片

整个index.html页面如下:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>index</title>
    <link rel="stylesheet" href="layui/css/layui.css">
</head>
<body class="layui-layout-body">
<div class="layui-layout layui-layout-admin">
    <div class="layui-header">
        <div class="layui-logo">layui 后台布局</div>
        <!-- 头部区域(可配合layui已有的水平导航) -->
        <ul class="layui-nav layui-layout-left">
            <li class="layui-nav-item"><a href="/role/list">角色</a></li>
            <li class="layui-nav-item"><a href="">权限</a></li>
            <li class="layui-nav-item"><a href="">用户</a></li>
            <li class="layui-nav-item">
                <a href="javascript:;">其它系统</a>
                <dl class="layui-nav-child">
                    <dd><a href="">邮件管理</a></dd>
                    <dd><a href="">消息管理</a></dd>
                    <dd><a href="">授权管理</a></dd>
                </dl>
            </li>
        </ul>
        <ul class="layui-nav layui-layout-right">
            <li class="layui-nav-item">
                <a href="javascript:;">
                    <img src="http://t.cn/RCzsdCq" class="layui-nav-img">
                    贤心
                </a>
                <dl class="layui-nav-child">
                    <dd><a href="">基本资料</a></dd>
                    <dd><a href="">安全设置</a></dd>
                </dl>
            </li>
            <li class="layui-nav-item"><a href="logout">退了</a></li>
        </ul>
    </div>
    <div class="layui-side layui-bg-black">
        <div class="layui-side-scroll">
            <!-- 左侧导航区域(可配合layui已有的垂直导航) -->
            <ul class="layui-nav layui-nav-tree"  lay-filter="test">
                <li class="layui-nav-item layui-nav-itemed">
                    <a class="" href="javascript:;">所有商品</a>
                    <dl class="layui-nav-child">
                        <dd><a href="javascript:;">列表一</a></dd>
                        <dd><a href="javascript:;">列表二</a></dd>
                        <dd><a href="javascript:;">列表三</a></dd>
                        <dd><a href="">超链接</a></dd>
                    </dl>
                </li>
                <li class="layui-nav-item">
                    <a href="javascript:;">解决方案</a>
                    <dl class="layui-nav-child">
                        <dd><a href="javascript:;">列表一</a></dd>
                        <dd><a href="javascript:;">列表二</a></dd>
                        <dd><a href="">超链接</a></dd>
                    </dl>
                </li>
                <li class="layui-nav-item"><a href="">云市场</a></li>
                <li class="layui-nav-item"><a href="">发布商品</a></li>
            </ul>
        </div>
    </div>
    <div class="layui-body">
        <!-- 内容主体区域 -->
        <div style="padding: 15px;">内容主体区域</div>
        <strong shiro:hasPermission="role:visitor">访客</strong>
        <H1 shiro:hasPermission="role:admin">是管理员</H1>
    </div>
    <div class="layui-footer">
        <!-- 底部固定区域 -->
        © layui.com - 底部固定区域
    </div>
</div>
<script src="layui/layui.js"></script>
<script>
    //JavaScript代码区域
    layui.use('element', function(){
        var element = layui.element;
    });
</script>
</body>
</html>

测试

重启项目,登录成功后进入首页,页面如下:

图片

”是管理员“这个标签内容显示了,而访客标签没有显示,说明对前端页面的权限控制加成功了

shiro实现session共享

这里要借助redis实现session共享,首先启动本地的redis。

添加redis依赖

<!--引入redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

application.yml添加redis相关配置

# redis配置(可以不用配置,下面配置的值都是默认的)
  redis:
    host: localhost
    port: 6379
    expireTime: 60000 #这个不是默认的

这里需要重写sessionDao,并且用redis作为缓存

添加类ShrioRedisCache类,代码如下:

@Component
public class ShiroRedisCache<K, V> implements Cache<K, V> {
    @Resource
    private RedisTemplate<K,V> redisTemplate;
    @Value("${spring.redis.expireTime}")
    private long expireTime;
    @Override
    public V get(K k) throws CacheException {
        return redisTemplate.opsForValue().get(k);
    }
    @Override
    public V put(K k, V v) throws CacheException {
        redisTemplate.opsForValue().set(k,v,expireTime, TimeUnit.SECONDS);
        return null;
    }
    @Override
    public V remove(K k) throws CacheException {
        V v = redisTemplate.opsForValue().get(k);
        redisTemplate.opsForValue().getOperations().delete(k);
        return v;
    }
    @Override
    public void clear() throws CacheException {
    }
    @Override
    public int size() {
        return 0;
    }
    @Override
    public Set<K> keys() {
        return null;
    }
    @Override
    public Collection<V> values() {
        return null;
    }
}

添加ShrioRedisCacheManager类,代码如下:

@Component
public class ShiroRedisCacheManager implements CacheManager {
    @Resource
    private Cache shiroRedisCache;
    @Override
    public <K, V> Cache<K, V> getCache(String s) throws CacheException {
        return shiroRedisCache;
    }
}

重新sessionDao,添加类,代码如下:

@Component
public class ShiroSessionDAO extends CachingSessionDAO {
    @Override
    protected void doUpdate(Session session) {
    }
    @Override
    protected void doDelete(Session session) {
    }
    @Override
    protected Serializable doCreate(Session session) {
        // 这里绑定sessionId到session,必须要有
        Serializable sessionId = generateSessionId(session);
        assignSessionId(session, sessionId);
        return sessionId;
    }
    @Override
    protected Session doReadSession(Serializable sessionId) {
        return null;
    }
}

修改shiro配置类ShrioConfig,添加sessionManager的bean

@Bean
public DefaultSessionManager sessionManager(SessionDAO sessionDAO){
    SimpleCookie simpleCookie = new SimpleCookie("shiro.sesssion"); // cookie名称
    simpleCookie.setMaxAge(6000); // cookie过期时间
    DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
    defaultWebSessionManager.setGlobalSessionTimeout(36000000);  // session过期时间,默认30分钟
    defaultWebSessionManager.setSessionIdCookie(simpleCookie);
    defaultWebSessionManager.setSessionDAO(sessionDAO);
    return defaultWebSessionManager;
}

再来把session管理器sessionManager和缓存管理器设置给shrio的安全管理器,ShiroConfig#securityManager()方法如下:

//  shiro的安全管理中心
@Bean
public SecurityManager securityManager(AuthorizingRealm customizeRealm, CacheManager shiroRedisCacheManager, SessionManager sessionManager) {
    DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
    defaultSecurityManager.setRealm(customizeRealm);
    defaultSecurityManager.setCacheManager(shiroRedisCacheManager);
    defaultSecurityManager.setSessionManager(sessionManager);
    return defaultSecurityManager;
}

测试
启动该项目两个实例,端口分别为8080,8081。我们请求8080的服务进行登录,再去访问8081服务的首页,如下:

图片

在去访问localhost:8081服务器的主页,就不用登录,可以直接访问到了

图片

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: springboot+shiro+jwt 是一种常见的后端技术组合,其中 springboot一个基于 Spring 框架的快速开发框架,shiro一个安全框架,用于身份验证、授权和加密等功能,jwt 是一种基于 token 的身份验证机制,可以用于前后端分离的应用中。这种技术组合可以帮助开发者快速搭建安全可靠的后端服务。 ### 回答2: Springboot一个开源的Java开发框架,是基于Spring框架的远程服务器控制技术方案,是现代化的全栈框架,集成丰富,提供了大量的开箱即用的组件,可以大大简化开发人员的开发工作。 Shiro一个强大的轻量级安全框架,支持用户身份识别、密码加密、权限控制、会话管理等一系列安全功能,被广泛应用于JavaWeb开发中。 JWT(JSON Web Token)是一种开放标准(RFC 7519),定义了一种简洁的、自包含的方式,用于通信双方之间以JSON对象的形式安全地传递信息。JWT可以用于状态管理和用户身份认证等场景。 在使用SpringBoot开发Web应用过程中,Shiro与JWT可以同时用于用户身份认证和权限控制。Shiro提供了一系列的身份识别、密码加密、权限控制、会话管理等功能,而JWT则可以实现无状态的身份认证。使用Shiro和JWT,可以有效地保护Web应用的安全,避免被恶意攻击者利用。 具体而言,使用Shiro和JWT可以将用户认证的主要逻辑统一在一起,实现更加优雅的认证和授权过程。同时,这样的组合也可以避免一些常见的安全漏洞,比如会话劫持、XSS攻击、CSRF等。 在实际开发中,使用SpringBoot Shiro JWT可以方便地进行二次开发,进一步优化开发成本和提高开发效率。同时,使用这个组合可以让开发者更好地专注于业务逻辑的开发,而无需关注安全问题,从而提高开发质量和开发人员的工作效率。 ### 回答3: Spring Boot是一种基于Spring框架的快速开发微服务的工具,能够使开发者可以快速构建基于Spring的应用程序。而Shiro一个强大易用的Java安全框架,可用于身份验证、权限控制、加密等。JWT(JSON Web Token)是一种基于JSON的安全令牌,可用于在客户端和服务器之间传递信息。 在使用Spring Boot开发Web应用程序时,通常需要进行用户身份验证和访问控制,这时候就可以使用Shiro来实现这些功能。同时,由于Web应用程序需要跨域访问,因此使用JWT可以方便地实现身份验证和授权的管理。 在使用Spring Boot和Shiro时,可以使用JWT作为身份验证和授权的管理工具。此时,需要进行以下几个步骤: 1.添加Shiro和JWT的依赖 在Spring Boot项目中,可以通过Maven或Gradle等工具添加Shiro和JWT的依赖。例如,可以添加以下依赖: <!-- Shiro依赖 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.7.0</version> </dependency> <!-- JWT依赖 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> 2.配置Shiro 在Spring Boot项目中,可以通过在application.properties或application.yml文件中进行Shiro的配置。例如,可以配置以下内容: # Shiro配置 shiro: user: loginUrl: /login # 登录页面URL jwt: secret: my_secret # JWT加密密钥 expiration: 1800000 # JWT过期时间,单位为毫秒,默认30分钟 3.创建Shiro的Realm 在Shiro中,Realm是用于从应用程序中获取安全数据(如用户、角色和权限)的组件。因此,需要创建Shiro的Realm,用于管理用户的认证和授权。 例如,可以创建一个自定义的Realm,其中包括从数据库中获取用户和角色的方法: public class MyRealm extends AuthorizingRealm { @Autowired private UserService userService; /** * 认证,验证用户身份 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); User user = userService.findByUsername(username); if (user == null) { throw new UnknownAccountException("用户名或密码错误"); } String password = user.getPassword(); return new SimpleAuthenticationInfo(user.getUsername(), password, getName()); } /** * 授权,验证用户的访问权限 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = (String) principals.getPrimaryPrincipal(); User user = userService.findByUsername(username); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); Set<String> roles = user.getRoles(); authorizationInfo.setRoles(roles); Set<String> permissions = user.getPermissions(); authorizationInfo.setStringPermissions(permissions); return authorizationInfo; } } 4.使用JWT进行身份验证和授权管理 在Spring Boot应用程序中,使用Shiro和JWT来进行身份验证和授权管理的流程大致如下: 4.1 前端用户进行登录操作,将用户名和密码发送到后台服务。 4.2 后台服务进行身份验证,将用户身份信息生成JWT并返回给前端。 4.3 前端保存JWT,并在后续的请求中将其发送到后台服务。 4.4 后台服务验证JWT的有效性,并根据用户的角色和权限信息进行访问控制。 综上所述,Spring Boot、Shiro和JWT可以很好地配合使用,实现Web应用程序的身份验证和授权管理。这种组合方案可以提高开发效率和系统安全性,同时也适用于微服务架构中对身份验证和授权的需求。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值