Apache Shiro 是一个开源的轻量级的 Java安全框架,它提供身份验证、授权、密码管理以及会话管理等功能。相对于 Spring Security,Shiro框架更加直观、易用,同时也能提供健壮的安全性。
在传统的 SSM框架中,手动整合 Shiro的配置步骤还是比较多的,针对 Spring Boot,Shiro官方提供了shiro-spring-boot-web-starter用来简化 Shiro在Spring Boot中的配置。下面通过样例进行演示。
1,添加依赖
首先编辑项目的 pom.xml文件,添加 Shiro依赖以及页面模板依赖:
shiro-spring-boot-web-starter:Shiro依赖
spring-boot-starter-thymeleaf:Thymeleaf依赖,本样例使用了 Thymeleaf模版。
thymeleaf-extras-shiro:实现在 Thymeleaf中使用 shiro标签。
注意:
这里不需要添加 spring-boot-starter-web依赖,因为 shiro-spring-boot-web-starter中已经依赖了 spring-boot-starter-web
org.apache.shiro
shiro-spring-boot-web-starter
1.4.0
org.springframework.boot
spring-boot-starter-thymeleaf
com.github.theborakompanioni
thymeleaf-extras-shiro
2.0.0
2,配置 Shiro
(1)首先在 application.properties 中配置 Shrio的基本信息:
# 开启 Shrio 配置,默认为 trueshiro.enabled=true# 开启 Shrio Web 配置,默认为 trueshiro.web.enabled=true# 配置登录地址,默认为"login.jsp"shiro.loginUrl=/login# 配置登录成功地址,默认为"/"shiro.successUrl=/index# 配置未获授权默认跳转地址shiro.unauthorizedUrl=/unauthorized# 是否允许通过 URL 参数实现会话跟踪,默认为 true。如果网站支持 Cookie,可以关闭次选项。shiro.sessionManager.sessionIdUrlRewritingEnabled=true# 是否允许通过 Cookie 实现会话跟踪,默认为 true。shiro.sessionManager.sessionIdCookieEnabled=true
(2)接着在 Java代码中配置 Shrio,其中最关键的是开头提供的两个 Bean:
@Configuration
public class ShiroConfig {
// 配置用户和角色,Realm 可以是自定义的 Realm,也可以是 Shiro 提供的 Realm
@Bean
public Realm realm() {
// 为简单起见,本案例没有配置数据库连接,直接配置了两个用户,以及对应的角色
TextConfigurationRealm realm = new TextConfigurationRealm();
realm.setUserDefinitions("admin=123,admin\n hangge=123,user");
// 配置角色权限,admin 具有read、write 权限,user 只有 read 权限
realm.setRoleDefinitions("admin=read,write\n user=read");
return realm;
}
// 配置基本的过滤规则
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition =
new DefaultShiroFilterChainDefinition();
// /login 和 /doLogin 可以匿名访问
chainDefinition.addPathDefinition("/login", "anon");
chainDefinition.addPathDefinition("/doLogin", "anon");
// /logout 是一个注销登录请求
chainDefinition.addPathDefinition("/logout", "logout");
// 其余请求则都需要人周后才能访问
chainDefinition.addPathDefinition("/**", "authc");
return chainDefinition;
}
// 这个Bean是为了支持在Thymeleaf中使用Shiro标签
// 如果不在Thymeleaf中使用Shiro标签,那么可以不提供 ShiroDialect
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
// 这个Bean的作用是使得@RequiresRoles和@RequiresPermissions注解生效
@Bean
public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator =
new DefaultAdvisorAutoProxyCreator();
// setUsePrefix(true)用于解决一个奇怪的bug。在引入spring aop的情况下。
// 在@Controller注解的类的方法中加入@RequiresRole等shiro注解,会导致该方法无法映射请求,
// 导致返回404,加入这项配置能解决这个bug
defaultAdvisorAutoProxyCreator.setUsePrefix(true);
return defaultAdvisorAutoProxyCreator;
}
}
3,配置登录接口以及页面访问接口
(1)创建一个 Controller用于配置登录接口以及相关页面的访问接口:
@Controller
public class UserController {
// 登录接口
@PostMapping("/doLogin")
public String doLogin(String username, String password, Model model) {
// 通过接收到的用户名和密码构造一个 UsernamePasswordToken 实例
UsernamePasswordToken token =
new UsernamePasswordToken(username, password);
// 获取一个Subject对象
Subject subject = SecurityUtils.getSubject();
try {
// 执行登录操作
subject.login(token);
} catch (AuthenticationException e) {
// 登录操作执行过程中,当有异常抛出时,说明登录失败,携带错误信息返回登录视图
model.addAttribute("error", "用户名或密码输入错误!");
return "login";
}
// 当登录成功时,则重定向到"/index"
return "redirect:/index";
}
// 暴露"/admin"接口,并且该接口需要具有admin角色才能访问
@RequiresRoles("admin")
@GetMapping("/admin")
public String admin() {
return "admin";
}
// 暴露"/user"接口,并且该接口只要有admin或者user角色就能访问
@RequiresRoles(value = {"admin","user"},logical = Logical.OR)
@GetMapping("/user")
public String user() {
return "user";
}
}
(2)对于其他不需要角色就能访问的接口,直接在 WebMvc中配置即可:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer{
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/index").setViewName("index");
registry.addViewController("/unauthorized").setViewName("unauthorized");
}
}
4,创建全局异常处理器
全局异常处理器可以进行全局异常处理,本样例主要是处理授权异常:
@ControllerAdvice
public class ExceptionController {
// 当用户访问未授权的资源时,跳转到 unauthorized 视图,并携带出错信息
@ExceptionHandler(AuthorizationException.class)
public ModelAndView error(AuthorizationException e) {
ModelAndView mv = new ModelAndView("unauthorized");
mv.addObject("error", e.getMessage());
return mv;
}
}
5,创建 HTML 页面
(1)login.html 是一个普通的登录页面,登录失败时通过一个 div显示登录失败信息:
Title
(2)index.html 是登录成功后的首先展示当前登录用户的登录名,以及一个“注销登录”链接,同时:
如果当前登录用户具备“admin”角色,则展示一个“管理员页面”的超链接
如果当前用户具备“admin”或者“user”角色,则展示一个“普通用户页面”的超链接
Title
Hello,
注销登录
管理员页面
普通用户页面
(3)admin.html是一个普通的管理员信息展示页面:
Title
管理员页面
(4)user.html是一个普通的用户信息展示页面:
Title
普通用户页面
(5)unauthorized.html是一个授权失败的展示页面,该页面还会展示授权出错的信息。
Title
未获授权,非法访问
6,运行测试
(1)启动项目,访问登录页(随便访问任意一个接口都会跳到登录页):
(2)使用 hangge用户登录,由于该用户不具备 admin角色,因此登录成功后的页面没有“管理员页面”超链接:
(3)点击“普通用户页面”链接即可进入普通用户页面:
(4)由于该用户不具备 admin角色,如果我手动在浏览器地址拦输入“http://localhost:8080/admin”访问管理员页面,则会跳转到未授权页面。