一、导入shiro的依赖
在pow.xml文件中添加以下依赖
可复制以下代码
<!-- 引入shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
二、在以下的项目结构下添加配置类和realm类
1、realm类内容
package com.woniuxy.realm;
import java.util.HashSet;
import java.util.Set;
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.springframework.beans.factory.annotation.Autowired;
import com.woniuxy.entity.User;
import com.woniuxy.service.UserService;
public class MyRealm extends AuthorizingRealm{
//注入一个service,这个service通过账号查询信息
@Autowired
UserService userService;
//用来从数据库中获取角色、权限的信息
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
System.out.println("正在获取角色权限信息。。。");
//通过当前用户的账号去数据库中查询出当前用户的角色信息
//假设通过账号找到了角色的信息,一个用户可以有多个角色
Set<String> roles = new HashSet<>();
roles.add("student");
//通过角色查询角色对应的权限
Set<String> perms = new HashSet<>();
perms.add("student:del");
//授权info
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(roles);
info.setStringPermissions(perms);
return info;
}
//获取认证(判断账号密码)的信息(账号密码)
//如果这个方法返回null,表示没找到对应的账号,shiro就会报账号不存在异常
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("====================");
System.out.println("正在进行认证...");
System.out.println("====================");
//获取账号,然后通过账号作为查询条件到数据库中查询账号信息(账号、密码等)
String account = (String) token.getPrincipal();
String pwd = new String((char[]) token.getCredentials());
System.out.println(account+"..."+pwd);
//调用serviece方法查询数据库
User user = userService.findUserByAccount(account);
//假设通过账号找到了一个信息
// User user = new User().setAccount("admin").setPwd("123456");
//创建认证info对象
if(user==null) {
return null;
}
SimpleAuthenticationInfo info =
new SimpleAuthenticationInfo(user.getAccount(),user.getPwd(),getName());
return info;
}
}
自己写的realm类需要继承AuthorizingRealm类,重写其中的两个方法:
doGetAuthorizationInfo:获取权限、角色信息
doGetAuthenticationInfo:获取认证(判断账号密码)的信息(账号密码)
2、配置类的内容:
package com.woniuxy.configuration;
import java.util.LinkedHashMap;
import java.util.Map;
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.woniuxy.realm.MyRealm;
@Configuration//该注解表示当前类是一个配置类,作用与xml是一样的
public class ShiroConfiguration {
//1、realm 域:作用用于从数据库中读取账号、角色、权限信息
@Bean//该bean的ID就是下面的方法名
public MyRealm myRealm() {
return new MyRealm();
}
//2、securitymanager 安全管理器:shiro核心
@Bean
public SecurityManager securityManager(MyRealm myRealm) {//相当于<property name='' ref='myRealm'></property>
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(myRealm);
return manager;
}
//3、shiro的过滤器:指定哪些操作需要认证,需要指定的权限
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
//创建过滤器的工厂对象
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
factoryBean.setSecurityManager(securityManager);
//安全配置
//设置登录页面
factoryBean.setLoginUrl("/login.html");
//没有权限时跳转到哪个页面
factoryBean.setUnauthorizedUrl("/error.html");
//认证成功之后跳转到指定的页面
factoryBean.setSuccessUrl("/index.html");
//******过滤器的配置******
Map<String, String> map = new LinkedHashMap<>();//有序的map
map.put("/index.html", "anon");//在登录/index.html 时 anon(匿名访问),不需要登录
map.put("/login.html", "anon");
map.put("/error.html", "anon");
//放行静态资源
map.put("/css/**", "anon");//表示放行css文件夹下的所有静态资源,/*代表当前目录下的文件,/**代表当前目录及其子目录的的文件
map.put("/js/**", "anon");
map.put("/html/**", "anon");
map.put("/layui/**", "anon");
map.put("/images/**", "anon");
map.put("/videos/**", "anon");
//动态请求放行
map.put("/phase/**", "anon");
map.put("/video/**", "anon");
map.put("/user/login", "anon");
/*
* 建议:能够用权限搞定的,就不要用角色。
*/
//权限设置访问url的角色
map.put("/student.html", "roles[student]");
map.put("/teacher.html", "roles[teacher]");
//设置访问url的权限
map.put("/student/del", "perms[student:del]");
//注销
map.put("/logout", "logout");
//除开以上的,其他的都需要认证
map.put("/**", "authc");//authc 认证authentication
//将map赋值给shiro过滤器
factoryBean.setFilterChainDefinitionMap(map);
return factoryBean;
}
}
在配置类中配置了shiro的三个重要部分:
realm域:是需要自己写的,也就是创建的realm类。
安全管理器(securitymanager):shiro自己有的,其中有一个属性是realm域,所以需要将自己写的realm域作为参数传入,目的是为其赋值。
shiro过滤器(ShiroFilter):shiro自己创建(通过工厂模式创建),过滤器中有一个属性行安全管理器,所以需要将安全管理器作为参数传入,目的也是为其赋值。
三、使用shiro,登录时的认证
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
@ResponseBody
public Result<Object> login(User user, String captcha) {
System.out.println(user);
// 认证
// 1、获取subject对象
Subject currentUser = SecurityUtils.getSubject();
Result<Object> result = new Result<>();
// 2、判断是否认证过
if (!currentUser.isAuthenticated()) {
// 没认证,创建令牌(令牌携带账号密码)
UsernamePasswordToken token = new UsernamePasswordToken(user.getAccount(), user.getPwd());
// 认证
try {
currentUser.login(token);
// 不报异常
System.out.println("认证成功");
result.setStatus(Result.SUCCESS);
return result;
} catch (UnknownAccountException e) {
System.out.println("账号不存在");
result.setStatus(Result.ERROR);
result.setMessage("账户不存在!");
return result;
} catch (IncorrectCredentialsException e) {
System.out.println("密码错误");
result.setStatus(Result.ERROR);
result.setMessage("密码错误!");
return result;
} catch (Exception e) {
System.out.println("其他异常");
result.setStatus(Result.ERROR);
result.setMessage("网络繁忙,请稍后再试!");
return result;
}
}
return result.setStatus(Result.ERROR).setMessage("你已经登录过了!");
}
}
四、在配置类中的过滤器总写请求需要的角色,权限等比较繁琐,可以通过注解的方式实现
1、在配置类中添加以下代码
//注解
@Bean
public AuthorizationAttributeSourceAdvisor
createAasa(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor aasa
= new AuthorizationAttributeSourceAdvisor();
aasa.setSecurityManager(securityManager);
return aasa;
}
//注解aop
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
2、注解的使用
//@RequiresRoles("student")//要求有student角色的人才能访问这个请求
//@RequiresRoles(value={"student","teacher"},logical=Logical.OR)//要求具备学生或者老师角色的用户可以访问这个请求
//@RequiresRoles(value= {"student","teacher"})//要求同时具有老师和学生角色的用户才可以使用该请求,logical(逻辑)不写默认为Logical.AND
//@RequiresPermissions("student:del")
@RequestMapping("/all")
@ResponseBody
public List<Phase> all(){
return phaseService.all();
}