Shiro(四) - SpringBoot 整合

表结构

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `pwd` varchar(255) DEFAULT NULL,
  `salt` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

INSERT INTO `user` VALUES ('1', 'captain', '36c952642de254f1b1872f321686f6c5', 'aH/CDyeNFN3DyZ3EXINXSQ==');

DatabaseRealm 类

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.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import top.qingrang.pojo.User;
import top.qingrang.service.UserService;

/**
 * DatabaseRealm 类,由 Shiro 调用
 * 通过数据库,验证用户,和相关授权的类,继承自 AuthorizingRealm,
 * 需重写 doGetAuthenticationInfo 和doGetAuthorizationInfo 两个方法。
 */
public class DatabaseRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    /**
     * 验证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 获取输入用户的账号和密码
        UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;
        String name = upToken.getPrincipal().toString();

        // 获取指定用户数据库中的密码,盐
        User user = userService.getUser(name);
        String pwdInDB = user.getPwd();
        String salt = user.getSalt();

        // 认证信息,存放账号,密码,盐,getName() 是当前 Realm 的继承方法,通常返回当前类名 :databaseRealm
        // 通过 配置类 ShiroConfiguration.java 里配置的 HashedCredentialsMatcher 进行自动校验
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                name, pwdInDB, ByteSource.Util.bytes(salt), getName());
        return authenticationInfo;
    }

    /**
     * 授权,能进入到这里,表示账号已经通过验证了
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
        return s;
    }
}

Shiro 配置类

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import top.qingrang.realm.DatabaseRealm;

/**
 * Shiro 配置
 */
@Configuration
public class ShiroConfiguration {

    /**
     * 保证实现了 shiro 内部 lifecycle 函数的 bean 执行
     */
    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 配置 shiro 的过滤器工厂类
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        return shiroFilterFactoryBean;
    }

    /**
     * 调用配置的权限管理器
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(getDatabaseRealm());
        return securityManager;
    }

    /**
     * 声明 DatabaseRealm 的使用
     */
    @Bean
    public DatabaseRealm getDatabaseRealm() {
        DatabaseRealm myShiroRealm = new DatabaseRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    /**
     * 密码匹配器,MD5 加密,加 2 次盐
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        hashedCredentialsMatcher.setHashIterations(2);
        return hashedCredentialsMatcher;
    }

    /**
     * 开启 shiro aop 注解支持
     *  使用代理方式,所以需要开启代码支持。
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

UserController - 注册,登录,注销

/**
 * 注册
 */
@GetMapping("/register")
public Object register() {
	String name = "zs";
	String pwd = "111";

	String salt = new SecureRandomNumberGenerator().nextBytes().toString(); // 随机创建盐
	int times = 2; // 2 次加密
	String algorithmName = "md5"; // MD5 加密方式
	// 加密 密码
	String encodedPassword = new SimpleHash(algorithmName, pwd, salt, times).toString();

	// 添加用户
	User user = new User();
	user.setName(name);
	user.setPwd(pwd);
	user.setSalt(salt);
	user.setPwd(encodedPassword);
	userService.insUser(user);

	return "register";
}

/**
 * 登录
 * @return 0 错误,1 正确
 */
@PostMapping(value = "/login")
public int login(@RequestBody User user, HttpSession session) throws Exception {
	String name = user.getName();
	name = HtmlUtils.htmlEscape(name);  // 转义 HTML 标签,防止恶意注册输入脚本
	Subject subject = SecurityUtils.getSubject(); // // 获取 subject 对象
	UsernamePasswordToken token = new UsernamePasswordToken(name, user.getPwd()); // 密码身份验证 Token
	// 调用 Subject.login 进行登录,如果失败将得到相应的 AuthenticationException 异常,根据异常提示用户错误信息;否则登录成功;
	try {
		subject.login(token);
		User u = userService.getUser();
		session.setAttribute("user", u);
		return 1;
	} catch (AuthenticationException e) {
		return 0;
	}
}

/**
 * 退出
 */
@GetMapping("/logout")
public String logout() {
	Subject subject = SecurityUtils.getSubject();
	if (subject.isAuthenticated())
		subject.logout();   // 调用 Subject.logout 进行退出操作
	return "logout";
}

配置登录拦截器

import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * 登录拦截器
 */
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        // 可以公开访问的目录,其它的 URL 都进行拦截控制
        String[] publicPages = new String[]{
                "login",
                "register",
                "/api/synDiary",
                "/api/dairiesByNewYear"
        };
        // 获取请求的 URL
        String uri = httpServletRequest.getRequestURI();
        // 获取项目根目录
        HttpSession session = httpServletRequest.getSession();
        String contextPath = session.getServletContext().getContextPath();
        // 只保留请求目录
        uri = StringUtils.remove(uri, contextPath + "/");

        // 若不是可公开访问的目录跳转登录页面
        if (!containWith(uri, publicPages)) {
            try {
                Subject subject = SecurityUtils.getSubject();
                // 如果没有登录,就跳转到登录页面
                if (!subject.isAuthenticated()) {
                    httpServletResponse.sendRedirect("login");
                    return false;
                }
            } catch (Exception e) {
                System.out.println(uri + " - " + e.getMessage());
            }
        }
        return true;
    }

    /**
     * 指定 uri 中是否包含 公开目录
     */
    private boolean containWith(String uri, String[] publicPages) {
        boolean result = false;
        for (String pages : publicPages) {
            if (uri.indexOf(pages) >= 0) {
                result = true;
                break;
            }
        }
        return result;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}

猜你喜欢

转载自blog.csdn.net/SGamble/article/details/88073484