角色有3个,管理员,老师,学生
1.管理员可访问(老师+学生的,主页,查询,删除)
2.老师可访问(老师的:主页,查询)(学生的:主页,删除)
3.学生可访问(学生的:主页,查询)
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>springboot_shiro_redisartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
<java.version>1.8java.version>
<shiro.version>1.8.0shiro.version>
properties>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.3.RELEASEversion>
<relativePath />
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.crazycakegroupId>
<artifactId>shiro-redis-spring-boot-starterartifactId>
<version>3.3.1version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>jcl-over-slf4jartifactId>
dependency>
dependencies>
project>
server:
port: 8088
spring:
thymeleaf:
cache: false
#--------------------shiro-redis配置------------------------
#redis地址
shiro-redis.redis-manager.host=127.0.0.1:6379
#redis密码
#shiro-redis.redis-manager.password=12345
#用户信息存入redis第几个库
shiro-redis.redis-manager.database=5
#实体类id,默认是id,找不到id则会报错
shiro-redis.cache-manager.principal-id-field-name=userId
#自定义redis关键字前缀的会话管理
shiro-redis.session-dao.key-prefix=token:user-session:
#自定义redis关键字前缀缓存管理
shiro-redis.cache-manager.key-prefix=token:authorization:
#--------------------shiro-redis配置------------------------
#---------shiro配置--------------------------------------
#登录页面
shiro.loginUrl=/toLogin.htm
#无权限访问
shiro.unauthorizedUrl=/error
shiro.userNativeSessionManager=true
#禁用URL会话重写
shiro.sessionManager.sessionIdUrlRewritingEnabled=false
#自定义cookie名字,默认JSESSIONID
shiro.sessionManager.cookie.name=Authorization
#---------shiro配置--------------------------------------
<configuration scan="false">
<property name="format" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %c %M: %n %replace(%caller{1}){'\t|Caller.{1}0| at|\r\n', ''} : %msg %n" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${format}pattern>
encoder>
appender>
<appender name="INFOLOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/DEBUGLOG/${HOSTNAME}_%d{yyyy-MM-dd}.%i.logFileNamePattern>
<cleanHistoryOnStart>truecleanHistoryOnStart>
<maxHistory>7maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>1MBmaxFileSize>
timeBasedFileNamingAndTriggeringPolicy>
rollingPolicy>
<encoder>
<pattern>[%date] [%thread] [%level] %msg%npattern>
<charset>UTF-8charset>
encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFOlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
appender>
<logger name="cn.itgsvip" level="info" >
<appender-ref ref="INFOLOG" />
logger>
<root level="info">
<appender-ref ref="STDOUT" />
root>
configuration>
package com.demo.vo;
import lombok.Data;
import java.io.Serializable;
/**
* @author 作者:wl
*/
@Data
public class UserInfoVO implements Serializable {
private static final long serialVersionUID = 1L;
private String userId;//用户id
private String userName;//用户名
private String password;//用户密码
}
package com.demo.service;
import com.demo.vo.UserInfoVO;
/**
* @author 作者:wl
*/
public interface UserInfoService {
//login登录
UserInfoVO login(String username, String password);
}
package com.demo.service.impl;
import org.springframework.stereotype.Service;
import com.demo.service.UserInfoService;
import com.demo.vo.UserInfoVO;
/**
* @author 作者:wl
*/
@Service
public class UserInfoServiceImpl implements UserInfoService {
//注入DAO实现数据库查询
@Override
public UserInfoVO login(String username, String password) {
UserInfoVO userInfo = new UserInfoVO();
/**模拟数据库账号,老师角色 **/
if("teacher".equals(username)) {
userInfo.setUserId("10001");
userInfo.setUserName(username);
userInfo.setPassword(password);
}
/**模拟数据库账号,学生角色 **/
if("student".equals(username)) {
userInfo.setUserId("10002");
userInfo.setUserName(username);
userInfo.setPassword(password);
}
/**模拟数据库账号,管理员 **/
if("admin".equals(username)) {
userInfo.setUserId("10003");
userInfo.setUserName(username);
userInfo.setPassword(password);
}
return userInfo;
}
}
package com.demo.config;
import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.config.web.autoconfigure.ShiroWebAutoConfiguration;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
@Configuration用于定义配置类,可替换xml配置文件,
被注解的类内部包含有一个或多个被@Bean注解的方法,
这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,
并用于构建bean定义,初始化Spring容器。
*/
@Configuration
public class BootShiroRedisAutoConfiguration extends ShiroWebAutoConfiguration {
@Autowired
RedisSessionDAO redisSessionDAO;
/**
* @方法名描述 : Shiro过滤器
*
*
* anon ===> 开放路径,允许匿名访问,不需要权限和不需要登录就可以访问
* authc ===> 需要登录后才可以访问路径
*
*
* @return
*/
@Bean("shiroFilterChainDefinition")
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
chainDefinition.addPathDefinition("/api/login.htm", "anon");
chainDefinition.addPathDefinition("/**", "authc");
return chainDefinition;
}
@Bean("authorizer")
public SampleRealm sampleRealm() {//把自定义Realm交给spring容器
return new SampleRealm();
}
@Override
protected SessionManager sessionManager() {
if (useNativeSessionManager) {
DefaultWebSessionManager nativeSessionManager = (DefaultWebSessionManager) nativeSessionManager();
//Redis Expire 命令用于设置 key 的过期时间,key 过期后将不再可用。单位以秒计。
//设置redissession失效时间,秒单位
redisSessionDAO.setExpire(60);
nativeSessionManager.setSessionDAO(redisSessionDAO);
return nativeSessionManager;
}
return new ServletContainerSessionManager();
}
}
package com.demo.config;
import java.util.HashSet;
import java.util.Set;
import com.demo.service.UserInfoService;
import com.demo.vo.UserInfoVO;
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.authc.UsernamePasswordToken;
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;
public class SampleRealm extends AuthorizingRealm {
@Autowired
private com.demo.service.UserInfoService UserInfoService;
//授权管理
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//principals 转成UserInfoVO对象
UserInfoVO user = (UserInfoVO) principals.getPrimaryPrincipal();
//利用SimpleAuthorizationInfo对象做授权
SimpleAuthorizationInfo sai = new SimpleAuthorizationInfo();
/** 模拟老师角色 **/
if("teacher".equals(user.getUserName())) {
System.out.println("teacher");
//授予对应角色的权限
sai.addRole("teacher");
//可以加集合权限
Set<String> permissions = new HashSet<String>();
permissions.add("teacher:find");
permissions.add("student:del");//删学生
sai.addStringPermissions(permissions);
}
/** 模拟学生角色 **/
if("student".equals(user.getUserName())) {
System.out.println("student");
//授予对应角色的权限
sai.addRole("student");
//可以加集合权限
Set<String> permissions = new HashSet<String>();
permissions.add("student:find");
sai.addStringPermissions(permissions);
}
/** 模拟管理员角色 **/
if("admin".equals(user.getUserName())) {
System.out.println("admin");
//授予对应角色的权限
sai.addRole("teacher");
sai.addRole("student");
//可以加集合权限
Set<String> permissions = new HashSet<String>();
permissions.add("teacher:find");//查老师
permissions.add("teacher:del");//删老师
permissions.add("student:find");//查学生
permissions.add("student:del");//删学生
sai.addStringPermissions(permissions);
}
return sai;
}
//认证管理
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException {
//取出controller的login方法里面根据username和password生成的token
UsernamePasswordToken token = (UsernamePasswordToken) authToken;
String username = token.getUsername();
String password = String.valueOf(token.getPassword());
//以此来做认证(调用业务来做认证)
//从数据库中取出账号密码
UserInfoVO userInfoVO = UserInfoService.login(username, password);
return new SimpleAuthenticationInfo(userInfoVO,password,getName());
}
}
package com.demo.config;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* @Auther:wl
*/
@ControllerAdvice
public class ShiroExceptionAdvice {
/**
* 无权限异常
* @param e AuthorizationException异常
* @return 403无权限页面
*/
@ExceptionHandler(AuthorizationException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public Object handleException(AuthorizationException e, Model model) {
System.out.println("AuthorizationException================>" + e.getMessage());
model.addAttribute("code", HttpStatus.FORBIDDEN.value());
return "403";
}
}
package com.demo.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author 作者:wl
* 需求:
* 不同账号登录,根据权限分配,可以访问不同的页面
* 如果越权访问,就显示无权访问.....
*/
@Controller
public class LoginController {
//跳转登录页面
@RequestMapping("toLogin.htm")
public String toLogin() {
return "login";
}
//shiro认证登录接口
@RequestMapping("api/login.htm")
public String apiLogin(String username, String password) {
/**
* 把账号密码交给shiro管理
* 以后可以根据账号密码来做认证和授权
* UsernamePasswordToken
* 可以基于账号密码生成一个token
* shiro可以利用token对subject对象进行认证和授权
*
*/
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
/** 登录,交给 shiro 处理,实际登录处理逻辑还是得自己写,处理了个寂寞 **/
SecurityUtils.getSubject().login(token);
return "index";//return "redirect:/index"; redirect可以省略不写
}
/**
* :退出接口
* @return 转发到登录页面
*/
@RequestMapping("api/logout")
public String logout() {
/** 退出,交给Shiro处理 **/
SecurityUtils.getSubject().logout();
return "redirect:/login";
}
}
package com.demo.controller;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 老师的Controller
* 1.管理员和老师可以访问主页
* 2.管理员和老师可以访问查询页
* 3.管理员可以访问删除
*/
@RestController
@RequestMapping("teacher")
public class TeacherController {
/**
* teacher角色可以访问的路径
* @return
*/
//属于 teacher 角色
@RequiresRoles("teacher")
@RequestMapping
public String adminIndex() {
return "老师的主页权限";
}
/**
* teacher:find权限可以访问的路径
* @return
*/
//符合teacher:find权限要求
@RequiresPermissions("teacher:find")
@RequestMapping("find")
public String adminFind() {
return "老师的查询权限";
}
/**
* 拥有 teacher和admin角色 和 teacher:del权限可以访问的路径
* @return
*/
//属于 teacher 或者 admin 之一;修改logical为OR 即可
@RequiresRoles(logical = Logical.OR, value = { "teacher", "admin" })
//符合teacher:del 权限要求
@RequiresPermissions("teacher:del")
@RequestMapping("del")
public String adminDel() {
return "老师的删除权限";
}
}
package com.demo.controller;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 学生的Controller
* 1.管理员和学生可以访问主页
* 2.管理员和学生可以访问查询页
* 3.管理员和老师可以访问删除
*/
@RestController
@RequestMapping("student")
public class StudentController {
/**
* teacher角色可以访问的路径
* @return
*/
@RequiresRoles("student")
@RequestMapping
public String adminIndex() {
return "学生的主页权限";
}
/**
* teacher:find权限可以访问的路径
* @return
*/
@RequiresPermissions("student:find")
@RequestMapping("find")
public String adminFind() {
return "学生的查询权限";
}
/**
* 拥有 teacher和admin角色 和 teacher:del权限可以访问的路径
* @return
*/
@RequiresRoles(logical = Logical.OR, value = { "teacher", "admin" })
@RequiresPermissions("student:del")
@RequestMapping("del")
public String adminDel() {
return "学生的删除权限";
}
}
DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head fragment="head">
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="viewport" content="width=device-width"/>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,300,400italic,400,600italic,600,700italic,700,800italic,800"
rel="stylesheet" type="text/css"/>
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"
type="text/css"/>
<style>
body {
margin-top: 60px;
}
.box {
padding: 50px;
text-align: center;
vertical-align: middle;
}
.custom-header {
border: 2px solid #3254a0;
}
style>
<body>
<div class="container">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<p>springboot快速整合shirop>
<table class="table">
<thead>
<tr>
<th>用户名th>
<th>密码th>
<th>角色th>
tr>
thead>
<tbody>
<tr>
<td>teachertd>
<td>123123td>
<td>老师td>
tr>
<tr>
<td>studenttd>
<td>123123td>
<td>学生td>
tr>
<tr>
<td>admintd>
<td>123123td>
<td>管理员td>
tr>
tbody>
table>
div>
div>
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">登录h3>
div>
<div class="panel-body">
<form name="loginform" action="api/login.htm" method="POST" accept-charset="UTF-8" role="form">
<fieldset>
<div class="form-group">
<input class="form-control" placeholder="请输入账号" name="username" type="text"/>
div>
<div class="form-group">
<input class="form-control" placeholder="请输入密码" name="password" type="password"/>
div>
<input class="btn btn-lg btn-success btn-block" type="submit" value="登录"/>
fieldset>
form>
div>
div>
div>
div>
div>
<script src="https://code.jquery.com/jquery.js">script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js">script>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js">script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js">script>
body>
html>
DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head fragment="head">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width" />
<link
href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,300,400italic,400,600italic,600,700italic,700,800italic,800"
rel="stylesheet" type="text/css" />
<link
href="https://netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"
rel="stylesheet" type="text/css" />
<style>