Springboot下, 一个shiro的Demo

准备工作


  • 一张用户表,需要字段:用户名、密码、权限

Shiro的配置类


ShiroConfig.java

package com.rain.demo.config;

import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shirFilter(DefaultWebSecurityManager securityManager) {

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 拦截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        // 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/index", "anon");
        filterChainDefinitionMap.put("/login", "anon");

        // 设置授权过滤器
        filterChainDefinitionMap.put("/add", "perms[1]");

        /* 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 */
        // authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
        filterChainDefinitionMap.put("/**", "authc");
        // 拦截后进入的url
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        // shiroFilterFactoryBean.setLoginUrl("/noLogin");
        // 登录成功后要跳转的链接
        // shiroFilterFactoryBean.setSuccessUrl("/index");

        // 未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean(name = "shiroRealm")
    public ShiroRealm shiroRealm() {
        return new ShiroRealm();
    }

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(ShiroRealm shiroRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setSessionManager(sessionManager());
        securityManager.setRealm(shiroRealm);
        securityManager.setRememberMeManager(rememberMeManager());
        securityManager.setCacheManager(cacheManager());
        return securityManager;
    }

    @Bean
    public EhCacheManager cacheManager() {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
        return ehCacheManager;
    }

    /**
     * shiro session的管理
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(sessionDAO());
        sessionManager.setGlobalSessionTimeout(2 * 60 * 60 * 1000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionIdCookieEnabled(true);
        SimpleCookie sessionIdCookie = new SimpleCookie("DEFAULT_SESSIONID");
        sessionIdCookie.setMaxAge(2 * 60 * 60);
        sessionManager.setSessionIdCookie(sessionIdCookie);
        return sessionManager;
    }

    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        SimpleCookie rememberMeCookie = new SimpleCookie("RememberMe");
        rememberMeCookie.setMaxAge(7 * 24 * 60 * 60);
        rememberMeManager.setCookie(rememberMeCookie);
        return rememberMeManager;
    }

    @Bean
    public EnterpriseCacheSessionDAO sessionDAO() {
        EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
        sessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
        sessionDAO.setSessionIdGenerator(sessionIdGenerator());
        return sessionDAO;
    }

    @Bean
    public JavaUuidSessionIdGenerator sessionIdGenerator() {
        return new JavaUuidSessionIdGenerator();
    }

    @Bean
    public ShiroLoginFilter shiroLoginFilter(){
        return new ShiroLoginFilter();
    }

}

Realm类


ShiroRealm.java

package com.rain.demo.config;

import com.rain.demo.entity.User;
import com.rain.demo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
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.apache.shiro.subject.Subject;

import javax.annotation.Resource;


public class ShiroRealm extends AuthorizingRealm {
    @Resource
    private UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("执行授权逻辑");
        // 给资源进行授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        // 添加资源的授权字符串
        info.addStringPermission("user:add");     // 直接添加授权字符串

        // 从数据库里取授权权限
        Subject subject = SecurityUtils.getSubject();

        User user = (User) subject.getPrincipal();

        info.addStringPermission(user.getRole().toString());

        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行认证逻辑");

        UsernamePasswordToken user = (UsernamePasswordToken) token;

        User currentUser = userService.getUser(user.getUsername());

        // 判断用户名
        if (currentUser == null) return null;
        // 判断密码
        else return new SimpleAuthenticationInfo(currentUser, currentUser.getPassword(), "");
    }
}

Controller类


Start.java

package com.rain.demo.controller;

import com.rain.demo.entity.Result;
import com.rain.demo.entity.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
@RequestMapping("/")
public class Start {

    @RequestMapping("login")
    @ResponseBody
    public Result login(User user){
        Result result = new Result();
	   result.setMessage("用户名不能为空");
	   if (null == user.getUsername())
	   	  return result;
	 /* 编写用户认证操作 */
        // 1、获取Subject
        Subject subject = SecurityUtils.getSubject();

        // 2、封装用户数据
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());

        try {
            // 3、执行登录方法
            subject.login(token);
        } catch (UnknownAccountException uae) {
            result.setMessage("未知账户");
        } catch (IncorrectCredentialsException ice) {
            result.setMessage("密码不正确");
        } catch (LockedAccountException lae) {
            result.setMessage("账户已锁定");
        } catch (ExcessiveAttemptsException eae) {
            result.setMessage("用户名或密码错误次数过多");
        } catch (AuthenticationException ae) {
            result.setMessage("用户名或密码不正确");
        }
        // 验证是否登录成功
        if (subject.isAuthenticated()) {
            result.setMessage("登录成功");
            System.out.println("登录成功");
        } else {
            token.clear();
            System.out.println("重新登录");
        }
        return result;
    }

    @RequestMapping("add")
    public String add(){
        return "add";
    }

    @RequestMapping("403")
    public String noAuth(){
        return "403";
    }

    @RequestMapping("update")
    public String update(){
        return "update";
    }

    @RequestMapping("errors")
    @ResponseBody
    public String error(){
        return "被拦截了";
    }

    @RequestMapping("index")
    public String index(){
        return "index";
    }
}

一个测试页面


index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="../static/jquery-3.3.1.min.js"></script>
    <title>Login</title>
</head>
<body>
这是登录页面 <br>
<a href="/add">add</a><br>
<a href="/update">update</a><br>
<a href="/logout">logout</a><br>

<button id="btn">点击登录</button>
<script>
    $(function () {
        $("#btn").on("click",function () {
            $.ajax({
                type:'get',
                url:'http://localhost:8089/login',
                dataType:'json',
                data:{
                    username : "123456",
                    password : "123"
                },
                success: function(res) {
                    console.log(res);
                    $("#msg").html(res.message);
                    //请求成功
                },
                error: function(err) {
                    console.log("error");
                }
            });
        });

    })
</script>
<div id="msg"></div>
</body>
</html>

一个避免Springboot拦截静态资源的配置类


SpringConfig.java

package com.rain.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class SpringConfig {
    @Configuration
    public class MyMvcConfig extends WebMvcConfigurationSupport {

        @Override
        protected void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
            super.addResourceHandlers(registry);
        }
    }
}

让页面跳转变成json


如果我们发送Ajax请求,shiro拦截后不是返回json数据,而是跳转到另一个页面,为了解决这个问题,需要我们重新写一个过滤器

ShiroLoginFilter.java

package com.rain.demo.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.rain.demo.entity.Result;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ShiroLoginFilter extends FormAuthenticationFilter {

    /**
     * 在访问controller前判断是否登录,返回json,不进行重定向。
     * @param request
     * @param response
     * @return true-继续往下执行,false-该filter过滤器已经处理,不继续执行其他过滤器
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        ObjectMapper mapper = new ObjectMapper();
        if (isAjax(request)) {
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setContentType("application/json");
            Result resultData = new Result();
            resultData.setMessage("登录认证失效,请重新登录!");
            httpServletResponse.getWriter().write(mapper.writeValueAsString(resultData));
        } else {
            // saveRequestAndRedirectToLogin(request, response);
            // 非ajax请求重定向为登录页面
            httpServletResponse.sendRedirect("/login");
        }
        return false;
    }

    private boolean isAjax(ServletRequest request){
        String header = ((HttpServletRequest) request).getHeader("X-Requested-With");
        if("XMLHttpRequest".equalsIgnoreCase(header)){
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

}

然后在ShiroConfig中加入

@Bean
public ShiroLoginFilter shiroLoginFilter(){
    return new ShiroLoginFilter();
}

可能用到的一些实体类


Result.java

package com.rain.demo.entity;

public class Result {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

User.java

package com.rain.demo.entity;

public class User {
    private Integer userId;

    private String username;

    private String password;

    private Integer role;

    private String isDelete;

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username == null ? null : username.trim();
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password == null ? null : password.trim();
    }

    public Integer getRole() {
        return role;
    }

    public void setRole(Integer role) {
        this.role = role;
    }

    public String getIsDelete() {
        return isDelete;
    }

    public void setIsDelete(String isDelete) {
        this.isDelete = isDelete == null ? null : isDelete.trim();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值