步骤一:导入依赖包
org.springframework.boot
spring-boot-starter-parent
2.0.6.RELEASE
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.2
com.github.pagehelper
pagehelper-spring-boot-starter
1.2.3
com.alibaba
druid-spring-boot-starter
1.1.10
junit
junit
test
io.springfox
springfox-swagger-ui
2.7.0
io.springfox
springfox-swagger2
2.7.0
org.csource
fastdfs-client-java
1.27-SNAPSHOT
redis.clients
jedis
org.springframework.data
spring-data-redis
org.apache.shiro
shiro-all
1.3.2
org.aspectj
aspectjrt
org.aspectj
aspectjweaver
net.sf.json-lib
json-lib
2.4
jdk15
步骤二:构建配置文件类ShiroConfig
package com.qf.fayuan.config;
import com.qf.fayuan.bean.MyShiroFactoryBean;
import com.qf.fayuan.mapper.PermissonMapper;
import com.qf.fayuan.mapper.UserMapper;
import com.qf.fayuan.shiro.realm.MyRealm;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
@Configuration
public class ShiroConfig {
@Autowired
private UserMapper userMapper;
@Autowired
private PermissonMapper permissonMapper;
@Bean
public DefaultWebSecurityManager securityManager(MyRealm myRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
return securityManager;
}
@Bean
public MyRealm myRealm(CredentialsMatcher credentialsMatcher, UserMapper userMapper,PermissonMapper permissonMapper){
//需要另外编写MyRealm类
MyRealm myRealm = new MyRealm();
myRealm.setUserMapper(userMapper);
myRealm.setCredentialsMatcher(credentialsMatcher);
myRealm.setPermissonMapper(permissonMapper);
return myRealm;
}
@Bean
public CredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashIterations(1024);
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
return hashedCredentialsMatcher;
}
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
public FilterRegistrationBean delegatingFilterProxy() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy("shiroFilter");
filterRegistrationBean.setFilter(delegatingFilterProxy);
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.setEnabled(true);
filterRegistrationBean.addInitParameter("targetFilterLifecycle", "true");
return filterRegistrationBean;
}
@Bean
public MyShiroFactoryBean shiroFilter(SecurityManager securityManager, PermissonMapper permissonMapper) {
//这个类也要另建
MyShiroFactoryBean myShiroFactoryBean = new MyShiroFactoryBean();
myShiroFactoryBean.setPermissonMapper(permissonMapper);
myShiroFactoryBean.setSecurityManager(securityManager);
return myShiroFactoryBean;
}
}
第三步:创建MyRealm类
package com.qf.fayuan.shiro.realm;
import com.qf.fayuan.mapper.PermissonMapper;
import com.qf.fayuan.mapper.UserMapper;
import com.qf.fayuan.shiro.pojo.CourtAdmin;
import com.qf.fayuan.shiro.usernamepasswordtoken.UserNamePassWordToken;
import com.qf.fayuan.user.pojo.User;
import com.qf.fayuan.vo.UserVo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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 java.util.List;
public class MyRealm extends AuthorizingRealm {
private UserMapper userMapper;
private PermissonMapper permissonMapper;
public void setPermissonMapper(PermissonMapper permissonMapper) {
this.permissonMapper = permissonMapper;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取用户的用户名
String username = ((UserNamePassWordToken) authenticationToken).getUsername();
//获取用户的角色
int role = ((UserNamePassWordToken) authenticationToken).getUser_role();
//根据用户名和用户角色,从数据库中查询该用户的信息
CourtAdmin courtAdmin = userMapper.getCourtAdmin(username,role);
//将用户存在数据库中的盐取出并且变成字节类型
ByteSource bytes = ByteSource.Util.bytes(courtAdmin.getSalt());
//传入用户名,用户在数据库中真实的的密码,盐,getName();
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,courtAdmin.getPassword(),bytes,getName());
//返回带有用户信息的对象
return simpleAuthenticationInfo;
}
}
步骤四:编写contorller层,实现用户的登录验证
// @LogAnno(operationtype = "/login",operationname = "用户登陆,服务器返回用户的部分信息")
@RequestMapping("/login")// @RequestParam(value = "user_role",required = false,defaultValue = "1")
public ResultBean login(String userinfo, Integer user_role, String password){
//获取当前登录用户的链接
Subject subject = SecurityUtils.getSubject();
if(!subject.isAuthenticated()){
//将用户的用户名和密码存入到UserNamePassWordToken中
UserNamePassWordToken usernamePasswordToken = new UserNamePassWordToken(userinfo,password,user_role);
try{
//验证登录用户的信息是否正确,如果失败的话就会报错处理,所以需要捕获异常
subject.login(usernamePasswordToken);
}catch (Exception e){
e.printStackTrace();
return ResultBean.setError(ErrorCodeInterface.MIMACUOWU,"信息错误",null);
}
}
UserVo userVo = userService.login(userinfo,user_role);
SecurityUtils.getSubject().getSession().setAttribute("user",userVo);
return ResultBean.setOk(userVo);
}
在上面中发现使用到了UserNamePassWordToken 这个对象,这个对象中可以携带几个字段,如username和password等,这个时候我想让这个对象也要携带我自定义的一个字段role,那么需要自定义一个类,让这个类去继承UserNamePassWordToken,然后添加一个新的字段role
类的内容如下:
package com.qf.fayuan.shiro.usernamepasswordtoken;
import org.apache.shiro.authc.UsernamePasswordToken;
public class UserNamePassWordToken extends UsernamePasswordToken {
private int user_role;
public UserNamePassWordToken(String username,String password,int user_role){
super(username,password);
this.user_role = user_role;
}
public int getUser_role() {
return user_role;
}
public void setUser_role(int user_role) {
this.user_role = user_role;
}
}
根据上面的内容就可以使用shiro实现简单的登录认证功能了。
步骤一:首先定义一个自定义注解,注解中包含,访问需要的权限(设置该路径名就是需要访问的权限) 、权限表述的相关信息
package com.qf.fayuan.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissonAnno {
String value();//权限的值,将方法或者类的路径设置为所要权限的值
String comment();//权限的描述 ,该权限的描述不应为空或者重复
boolean isMenu() default false;//判断是否是一个表单
String parent(); //其父类的权限描述
}
步骤二:将注解添加到类和方法的上面
@PermissonAnno(value = "/getnoticeinfo",comment="查询公告",isMenu = false,parent = "公告列表")
步骤三:遍历每一个类和类中的每一个方法,将权限添加到表中
首先定义一个工具类来封装注解中的信息
package com.qf.fayuan.shiro.pojo;
public class PermissonBean {
private int id;
private String value;
private String comment;
private boolean isMenu;
private String parent;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public boolean isMenu() {
return isMenu;
}
public void setMenu(boolean menu) {
isMenu = menu;
}
public String getParent() {
return parent;
}
public void setParent(String parent) {
this.parent = parent;
}
@Override
public String toString() {
return "PermissonBean{" +
"id=" + id +
", value='" + value + '\'' +
", comment='" + comment + '\'' +
", isMenu=" + isMenu +
", parent='" + parent + '\'' +
'}';
}
}
然后通过给定一个项目的路径,就可以扫描这个项目中的所以类和方法
package com.qf.fayuan.utils;
import com.qf.fayuan.anno.PermissonAnno;
import com.qf.fayuan.shiro.pojo.PermissonBean;
import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
public class PermissonAnnoUtils {
// 定义一个set集合用来存储判断是含有该注解的类
private static Set classSet = new HashSet<>();
//将直接定义在类上面的注解的信息封装到对象,然后存入到该集合中
private static Set classBean = new LinkedHashSet<>();
//将定义在方法上的注解的信息封装到对象中然后存入到该集合中
private static Set methodBean = new LinkedHashSet<>();
public static void getClassSet(String packagename) throws Exception {
/**
* xxx.class.getResource()用来从当前类(xxx)所在的目录下(也就是以当前类所在路径为根路径)获得资源;
* xxx.class.getClassLoader().getResource()用来从classpath路径下(也就是以classpath所在路径为根路径)获得资源。
*/
//根据项目中一段路径(com.qf.fayuan),获得 file:/D:/idea/idea-workspace/fayuanxiangmu/target/classes/com/qf/fayuan 其中file为协议
URL resource = PermissonAnnoUtils.class.getClassLoader().getResource(packagename.replace(".", "/"));
if(resource!=null||"file".equalsIgnoreCase(resource.getProtocol())){
//getProtocol获取协议:也就是 file: 部分
String packagepath = URLDecoder.decode(resource.getFile(),"utf-8");
///D:/idea/idea-workspace/fayuanxiangmu/target/classes/com/qf/fayuan
addClass(packagename,packagepath);
}
}
private static void addClass(String packagename, String packagepath) throws Exception {
//获得该路径下的所有文件,并且添加一个过滤器,设置条件
File[] files = new File(packagepath).listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
//如果是一个文件或者是一个类,或者是一个目录,就遍历进入数组中
return (pathname.isFile() || pathname.getName().endsWith(".class") || pathname.isDirectory());
}
});
for (File file : files) {
//file的格式为: D:\idea\idea-workspace\fayuanxiangmu\target\classes\com\qf\fayuan\anno
//获得文件的名字
String name = file.getName();
//name 的值是 anno
if(file.isFile()){
if(!name.contains("Controller")){
continue;
}
String classname = name.substring(0,name.lastIndexOf("."));
classname = packagename + "." +classname;
doAddClass(classname);
}else {
String subpackagepath = name;
if(!StringUtils.EmptyString(packagepath)){
subpackagepath = packagepath+"/"+name;
}
String subpackagename = name;
if(!StringUtils.EmptyString(packagename)){
subpackagename = packagename+"."+name;
}
addClass(subpackagename,subpackagepath);
}
}
}
private static void doAddClass(String classname) throws ClassNotFoundException {
Class> aClass = Class.forName(classname);
classSet.add(aClass);
}
public static void inject(String packagename) throws Exception {
classSet.clear();
classBean.clear();
methodBean.clear();
getClassSet(packagename);
for (Class clazz : classSet) {
//扫描类上的注解
PermissonAnno annotation = (PermissonAnno) clazz.getAnnotation(PermissonAnno.class);
String value = null;
String comment = null;
boolean isMenu = false;
String parent = null;
String parentValue = null;
if(annotation!=null){
comment = annotation.comment();
parentValue = annotation.value();
isMenu = annotation.isMenu();
parent = annotation.parent();
PermissonBean permissonBean = new PermissonBean();
permissonBean.setComment(comment);
permissonBean.setValue(parentValue);
permissonBean.setMenu(isMenu);
permissonBean.setParent(parent);
classBean.add(permissonBean);
}
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//扫描方法上的注解
PermissonAnno annotation1 = method.getAnnotation(PermissonAnno.class);
if(annotation1!=null){
value = annotation1.value();
comment = annotation1.comment();
isMenu = annotation1.isMenu();
parent = annotation1.parent();
value = parentValue+value;
PermissonBean permissonBean = new PermissonBean();
permissonBean.setValue(value);
permissonBean.setComment(comment);
permissonBean.setMenu(isMenu);
permissonBean.setParent(parent);
methodBean.add(permissonBean);
}
}
}
}
public static Set getClassSet() {
return classSet;
}
public static Set getClassBean() {
return classBean;
}
public static Set getMethodBean() {
return methodBean;
}
public static void main(String[] args) {
File[] files = new File("D:/idea/idea-workspace/fayuanxiangmu/target/classes/com/qf/fayuan").listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
System.out.println(pathname);//D:\idea\idea-workspace\fayuanxiangmu\target\classes\com\qf\fayuan\anno
System.out.println(pathname.getName());//anno
return (pathname.isFile() || pathname.getName().endsWith(".class") || pathname.isDirectory());
}
});
for (File file : files) {
System.out.println(file.getName());
}
//
// URL resource = PermissonAnnoUtils.class.getClassLoader().getResource("com.qf.fayuan".replace(".", "/"));
// System.out.println(resource);//file:/D:/idea/idea-workspace/fayuanxiangmu/target/classes/com/qf/fayuan
//
// URL resource1 = PermissonAnnoUtils.class.getResource("com.qf.fayuan");
// System.out.println(resource1);//null
//
// String file = PermissonAnnoUtils.class.getClassLoader().getResource("com.qf.fayuan".replace(".", "/")).getFile();
// System.out.println(file);///D:/idea/idea-workspace/fayuanxiangmu/target/classes/com/qf/fayuan
}
}
别写controller层,访问该层,将所有的注解中的信息存入到数据库中
package com.qf.fayuan.shiro.controller;
import com.qf.fayuan.anno.PermissonAnno;
import com.qf.fayuan.bean.ResultBean;
import com.qf.fayuan.mapper.PermissonMapper;
import com.qf.fayuan.shiro.pojo.PermissonBean;
import com.qf.fayuan.utils.ErrorCodeInterface;
import com.qf.fayuan.utils.PermissonAnnoUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Set;
@RequestMapping("/permisson")
@RestController()
public class PermissonController {
@Autowired
private PermissonMapper permissonMapper;
@RequestMapping("/addallpermisson")
public ResultBean addAllPermisson(){
try {
PermissonAnnoUtils.inject("com.qf.fayuan");
Set classBean = PermissonAnnoUtils.getClassBean();
permissonMapper.addAllPermisson(classBean);
Set methodBean = PermissonAnnoUtils.getMethodBean();
permissonMapper.addAllPermisson(methodBean);
} catch (Exception e) {
e.printStackTrace();
return ResultBean.setError(ErrorCodeInterface.XIUGAIMIMASHIBAI,"失败了",null);
}
return ResultBean.setOk(null);
}
}
步骤四:上面将所有的权限添加到了数据库中,下面就是将访问各个类或者方法的权限添加到shiro中
在MyShiroFactoryBean中添加代码即可
package com.qf.fayuan.bean;
import com.qf.fayuan.mapper.PermissonMapper;
import com.qf.fayuan.shiro.pojo.PermissonValue;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import javax.annotation.PostConstruct;
import java.util.LinkedHashMap;
import java.util.List;
public class MyShiroFactoryBean extends ShiroFilterFactoryBean {
//这里不应该使用自动注入的方式
private PermissonMapper permissonMapper;
public void setPermissonMapper(PermissonMapper permissonMapper) {
this.permissonMapper = permissonMapper;
}
//添加这个注解以为着
@PostConstruct
public void init(){
//设置登录的界面的位置
setLoginUrl("/login.html");
//创建一个map集合,将要访问的路径和所需要的权限添加进去
LinkedHashMap perms = new LinkedHashMap<>();
//访问首页,需要的权限是任何权限都可以访问
perms.put("/index.html","anon");
perms.put("/login.html","anon");
//如果想访问success.html页面,必须进行登录认证,而且会自动跳转到登录界面
perms.put("/success.html","authc");
//将本项目的所有权限添加到添加到shiro中
List list = permissonMapper.getAllPermisson();
if(list!=null){
for (PermissonValue value : list) {
//注意添加权限的时候,权限的书写格式
perms.put(value.getValue(),"perms["+value.getValue()+"]");
}
}
setFilterChainDefinitionMap(perms);
}
}
这样,就设置好了访问每个页面时所需要的权限,但是只有这些还不够,因为,只是知道了访问这些界面所需要的权限,但是没有给相应用户添加权限,否则,这些界面用户是无法访问的
步骤五:将用户的所有权限遍历出来放到shiro中,让shiro自行比对
在doGetAuthorizationInfo方法中
package com.qf.fayuan.shiro.realm;
import com.qf.fayuan.mapper.PermissonMapper;
import com.qf.fayuan.mapper.UserMapper;
import com.qf.fayuan.shiro.pojo.CourtAdmin;
import com.qf.fayuan.shiro.usernamepasswordtoken.UserNamePassWordToken;
import com.qf.fayuan.user.pojo.User;
import com.qf.fayuan.vo.UserVo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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 java.util.List;
public class MyRealm extends AuthorizingRealm {
private UserMapper userMapper;
private PermissonMapper permissonMapper;
public void setPermissonMapper(PermissonMapper permissonMapper) {
this.permissonMapper = permissonMapper;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
UserVo user = (UserVo) SecurityUtils.getSubject().getSession().getAttribute("user");
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//获取用户的所有权限
List list = userMapper.getSelfPermisson(user.getUser_id());
if(list!=null){
for (String s : list) {
//将所有的权限添加到shiro中
simpleAuthorizationInfo.addStringPermission(s);
}
}
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = ((UserNamePassWordToken) authenticationToken).getUsername();
int role = ((UserNamePassWordToken) authenticationToken).getUser_role();
CourtAdmin courtAdmin = userMapper.getCourtAdmin(username,role);
ByteSource bytes = ByteSource.Util.bytes(courtAdmin.getSalt());
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,courtAdmin.getPassword(),bytes,getName());
return simpleAuthenticationInfo;
}
}
至此权限部分完成