为什么要记录,后续写我的感受的时候,会说。
那话不多说,直接上代码。使用模板新建项目,会话管理还没加入。
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
没赋予指定角色时,不能访问此方法
没赋予指定权限时,不能访问此方法
//缓存 难点 后续再研究
爱生活、爱分享、爱康宝