记录spring boot整合shiro

为什么要记录,后续写我的感受的时候,会说。

那话不多说,直接上代码。使用模板新建项目,会话管理还没加入。

1.完善pom.xml文件,放些重要的。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.5.3</version>
        </dependency>
        <!-- 页面模板依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!--热部署依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>

2.application.yml文件,基本不改,想换下端口的,可以添加

3.启动类,这里也不加代码了,自动生成即可

4.编写LoginController.java。

package com.shenk.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
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;

import com.shenk.bean.User;

@Controller
public class LoginController {
	
	@RequestMapping("login")
	@ResponseBody
	public String login() {
		return "login";
	}
	
	@RequestMapping("/checkUser")
	public String checkUser(User user){
        //添加用户认证信息
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
                user.getUserName(),
                user.getPassword()
        );
        try {
            //进行验证,这里可以捕获异常,然后返回对应信息
            subject.login(usernamePasswordToken);
            System.out.println(subject.getPrincipal());
        } catch (AuthenticationException e) {
//            e.printStackTrace();
            return "账号或密码错误!";
        } catch (UnauthorizedException e) {
//            e.printStackTrace();
            return "没有相应角色,不能访问";
        } catch (AuthorizationException e) {
//            e.printStackTrace();
            return "没有相应权限,不能访问";
        }
        return "index";
    }
	
	@RequestMapping("logout")
	@ResponseBody
	public String logout() {
		Subject subject = SecurityUtils.getSubject();
		subject.logout();
		return "logout";
	}
}

5.编写IndexController.java

package com.shenk.controller;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/home")
public class IndexController {
	
	@RequiresRoles("admin")//验证角色是否一致,若登录用户不是此角色,直接返回没有权限
	@RequiresPermissions("add")//验证权限是否一致。若admin没有此权限,不能访问
	@RequestMapping("/add")
	@ResponseBody
	public String add() {
		//返回字符串至页面
		return "add";
	}
	
	@RequiresRoles("admin")//验证角色是否一致,若登录用户不是此角色,直接返回没有权限
	@RequestMapping("index")
	public String index() {
		//返回 index视图,映射至index.html
		return "index";
	}
	@RequestMapping("/test")
	@ResponseBody
	public String test() {
		return "test";
	}
}

6.编写一下实体User.java

package com.shenk.bean;

public class User {
	private String userName;
	private String password;
	
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	
}

7.编写MyShiroRealm.java

package com.shenk.shiro;

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.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 com.shenk.common.DigestUtils;

public class MyShiroRealm extends AuthorizingRealm {

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		// 授权	可以根据用户查询数据库,查到对应的角色和权限,并赋值。这里 使用模拟数据
		System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");
//		String name = (String) principals.getPrimaryPrincipal();
		String name = (String) SecurityUtils.getSubject().getPrincipal();
		//添加角色和权限
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		if(name.equals("admin")) {
			simpleAuthorizationInfo.addRole("admin");
			//多权限可自行配置
			simpleAuthorizationInfo.addStringPermission("add");
		} else if(name.equals("shenk")) {
			simpleAuthorizationInfo.addRole("admin");
		} else {
			simpleAuthorizationInfo.addRole("test");
		}
		return simpleAuthorizationInfo;
	}

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
		// 登录认证
		//加这一步的目的是在Post请求的时候会先进认证,然后在到请求
        if (authenticationToken.getPrincipal() == null) {
            return null;
        }
        //获取用户信息
        String name = authenticationToken.getPrincipal().toString();
        
        //查询用户是否在数据库中,账号和密码是否匹配。这里使用模拟
       
        //这里验证authenticationToken和simpleAuthenticationInfo的信息
//        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, "123456", getName());
        String password = DigestUtils.Md5(name, "123456");
        //第一个参数:可以传用户对象、也可以传用户名。
        //第二个参数:传的是从数据库中获取的password,然后再与token中的password进行对比,匹配上了就通过,匹配不上就报异常
        /*第三个参数,盐–用于加密密码对比,–获取的经验:为了防止两用户的初始密码是一样的,
		四个参数,防止两用户可能初始密码相同时候用,token 用simplehash(四个参数的构造) 加密默认用了MD5 迭代一次加密,
		info中在密码比对调用new SimpleHash(String algorithmName, Object source)这个实例化对象默认迭代一次了,
		所以当你用三个参数加密时候可能两 个初始密码相同人的就没能区别开 (因此realm中密码要从数据库的查的原因),
		通过设置reaml 中credentialsMatcher 属性的各项属性可实现
         */
        //当前realm的名字
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, password,ByteSource.Util.bytes(name),  getName());
        return simpleAuthenticationInfo;
	}

}

8.编写ShiroConfig.java

package com.shenk.config;

import java.util.HashMap;
import java.util.Map;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
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.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.shenk.shiro.MyShiroRealm;

@Configuration
public class ShiroConfig {

    //将自己的验证方式加入容器
    @Bean
    public MyShiroRealm myShiroRealm() {
    	MyShiroRealm myShiroRealm = new MyShiroRealm();
    	myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    //权限管理,配置主要是Realm的管理认证
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }
    
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2);// 散列的次数,比如散列两次,相当于 // md5(md5(""));
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return hashedCredentialsMatcher;
    }

    //Filter工厂,设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> map = new HashMap<>();
        // 放行登录页面
        map.put("/login", "anon");
        // 放行登录提交请求
        map.put("/checkUser", "anon");
        //登出
        map.put("/logout", "logout");
        //对所有用户认证
        map.put("/**", "authc");
        //登录
        shiroFilterFactoryBean.setLoginUrl("/login");
        //首页
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //错误页面,认证不通过跳转
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    //加入注解的使用,不加入这个注解不生效。若加了还是不生效,应该是aop没起作用,再加上下面的配置
    //这是一个Spring Advisor,用于拦截shiro的几个角色、权限注解,如RequiresRoles,RequiresPermissions。
  	//Spring AOP(Pointcut,Advice,Advisor)
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    
    //注解生效
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }
}

9.编写DigestUtils.java

package com.shenk.common;

import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.util.ByteSource;

public class DigestUtils {
	public static String Md5(String userName,String password){
        Md5Hash hash = new Md5Hash(password, ByteSource.Util.bytes(userName), 2);
        return hash.toString();
    }
	
	public static void main(String[] args) {
		System.out.println(Md5("admin","123456"));
	}
}

10.编写MyExceptionHandler.java

package com.shenk.filter;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class MyExceptionHandler {
	private Logger logger = LoggerFactory.getLogger(this.getClass());

    @ExceptionHandler
    public String ErrorHandler(AuthorizationException e) {
        logger.info("没有通过权限验证!");
        return "error";
    }
    @ExceptionHandler
    public String ErrorHandler(UnauthorizedException e) {
        logger.info("没有通过角色验证!");
        return "error";
    }
    @ExceptionHandler
    public String Error(AuthenticationException e) {
        logger.info("账号密码不匹配!");
        return "error";
    }
}

11.index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    index页面,热部署
</body>
</html>

12.error.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h1>没有权限</h1>
</body>
</html>

13.测试说明.txt

验证shiro授权

http://localhost:8080/login

http://localhost:8080/logout

http://localhost:8080/home/add

http://localhost:8080/home/index

http://localhost:8080/home/test

http://localhost:8080/checkUser?userName=admin&password=123456

http://localhost:8080/checkUser?userName=shenk&password=123456

http://localhost:8080/checkUser?userName=test&password=123456

验证
不登录时,访问url会进行拦截过滤到login
没赋予指定角色时,不能访问此方法
没赋予指定权限时,不能访问此方法

//缓存 难点 后续再研究

爱生活、爱分享、爱康宝

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值