Spring Boot整合shiro - Go语言中文社区

Spring Boot整合shiro


shiro面世已经有很长时间了,相比Spring security更加精简一些,也更容易上手,因此就想分享下这段时间的学习成果,就当是学习笔记了

1 pom文件如下

<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.0</modelVersion>

  <groupId>net.axcf</groupId>
  <artifactId>ShiroSample</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>ShiroSample</name>
  <url>http://maven.apache.org</url>
  
  

  <properties>
    	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
  </properties>
  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.6.RELEASE</version>
  </parent>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    
    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
           
        </dependency>
        <dependency>
		<groupId>org.mybatis.spring.boot</groupId>
		<artifactId>mybatis-spring-boot-starter</artifactId>
		<version>1.3.1</version>
	</dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            
        </dependency>
       <dependency>
	   <groupId>mysql</groupId>
	   <artifactId>mysql-connector-java</artifactId>
	   <version>5.1.46</version>
	</dependency>
         <dependency>  
            <groupId>org.apache.shiro</groupId>  
            <artifactId>shiro-core</artifactId>  
            <version>1.3.2</version>  
        </dependency> 
       <dependency>
           <groupId>org.apache.shiro</groupId>
           <artifactId>shiro-spring</artifactId>
           <version>1.3.2</version>
       </dependency>
        <!-- shiro ehcache -->
       <dependency>
           <groupId>org.apache.shiro</groupId>
           <artifactId>shiro-ehcache</artifactId>
           <version>1.3.2</version>
       </dependency>
       <dependency>
	  <groupId>org.springframework.boot</groupId>
	  <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
    <!-- servlet依赖. -->
     <dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>javax.servlet-api</artifactId>
	<scope>provided</scope>
      </dependency>
		
      <dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>jstl</artifactId>
       </dependency>
      <!-- tomcat的支持.-->
	<dependency>
	  <groupId>org.springframework.boot</groupId>
	  <artifactId>spring-boot-starter-tomcat</artifactId>
	   <scope>provided</scope>
	</dependency>
	<dependency>
	    <groupId>org.apache.tomcat.embed</groupId>
	    <artifactId>tomcat-embed-jasper</artifactId>
	    <!--<scope>provided</scope>-->
	</dependency>
	<dependency>
	   <groupId>net.mingsoft</groupId>
           <artifactId>shiro-freemarker-tags</artifactId>
            <version>1.0.0</version>
       </dependency>

  </dependencies>
  
</project>

2  下面介绍shiro比较重要的Realm,先上代码

package com.debug.shiro;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

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.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import com.debug.dto.UserAuthInfo;
import com.debug.entity.user.User;
import com.debug.service.UserService;

public class MyShiroRealm extends AuthorizingRealm{
     
	@Resource
	private UserService userService;
	
	/* 
	 * 授权
	 */
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		Object principal=principals.getPrimaryPrincipal();
		
		Set<String> roleSet=new HashSet<String>();
		 //添加角色和权限
                SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        
                 //获取用户对应的权限
                List<UserAuthInfo> auth=userService.findUserAuth(principal.toString());
                Iterator<UserAuthInfo> it=auth.iterator();
                Iterator<UserAuthInfo> permission=auth.iterator();
        
                while(it.hasNext()) {
        	    UserAuthInfo info=it.next();
        	    //添加角色
        	    simpleAuthorizationInfo.addRole(info.getRname());
        	    //添加权限
        	    while(permission.hasNext()) {
        		UserAuthInfo pinfo=permission.next();
        		simpleAuthorizationInfo.addStringPermission(pinfo.getMname());
        	     }
                }
		return simpleAuthorizationInfo;
	}

	
	/* 
	 * 认证
	 */
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		UsernamePasswordToken utoken=(UsernamePasswordToken)token;
		String userName=utoken.getUsername();
		User u=userService.findByUserName(userName);
		
		ByteSource salt=ByteSource.Util.bytes(userName);
		if(u==null) {
			return null;
		}else {
			 //SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName, u.getPassword().toString(), getName());
			SimpleAuthenticationInfo simpleAuthenticationInfo=new SimpleAuthenticationInfo(userName, u.getPassword(), salt, this.getName());
			return simpleAuthenticationInfo;
		}
		
	}
	
	public Object getHashedCredential(String pswd) {
		String algorithmName="MD5";
		Object password=pswd;
		int hashIterations=1024;
		ByteSource salt=ByteSource.Util.bytes("guest");
		Object res=new SimpleHash(algorithmName, password, salt, hashIterations);
		return res;
	}

}

上面的2个方法是核心部分,这里把用户、角色、权限放到了mysql中,主要涉及到五张表:用户表,角色表(用户所拥有的角色),权限表(角色所涉及到的权限),用户-角色表(用户和角色是多对多的),角色-权限表(角色和权限是多对多的),表设计可以参考这里:https://www.cnblogs.com/hlhdidi/p/6376457.html

这里使用的是mybatis所以把主要的sql贴出来给大家:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >  
<mapper namespace="com.debug.dao.UserDao" >  
        <resultMap id="BaseResultMap" type="com.debug.entity.user.User" >  
                    <id column="uid" property="uid"  />
		    <result column="username" property="username" />
		    <result column="password" property="password" />
        </resultMap>  
        
         <resultMap id="authResultMap" type="com.debug.dto.UserAuthInfo" >  
                    <id column="uid" property="uid"  />
		    <result column="username" property="username" />
		    <result column="password" property="password" />
		    <result column="mid" property="mid" />
		    <result column="mname" property="mname" />
        </resultMap>  
     
      <select id="findByUserName" parameterType="string" resultType="com.debug.entity.user.User">
      
        select * from user where username=#{userName}
      </select>
      
      <select id="findUserAuth" parameterType="string" resultMap="authResultMap">
        select A.*,B.rid,B.rname,C.mid,C.mname from user A,role B,module C,module_role D,user_role E 
        where A.uid=E.uid and B.rid=E.rid and B.rid=D.rid and C.mid=D.mid and D.rid=B.rid
        and A.username=#{username}
      </select>
</mapper>  

这次重点放在shiro上,其他技术不做详细讨论

3 凭证比对器(按需编写,可加入盐值加密)

package com.debug.shiro;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;

public class MyCredentialsMatcher extends SimpleCredentialsMatcher{
	public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
		
        UsernamePasswordToken utoken=(UsernamePasswordToken) token;
        //获得用户输入的用户名、密码:(采用加盐(salt)的方式去检验)
        String userName = new String(utoken.getUsername());
        String password = new String(utoken.getPassword());
        
        //获得数据库中的密码
        String dbPassword=(String) info.getCredentials();
        
        ByteSource salt=ByteSource.Util.bytes(userName);
	Object res=new SimpleHash("MD5", password, salt, 1024);
		
	if(res.toString().equals(dbPassword)) {
	    return true;
	}else {
	   return false;
	}
        
        //进行密码的比对
        //return this.equals(inPassword, dbPassword);
    }
}

盐值根据需要选择可以是id,userName或者其他,加密方式可以是MD5或者SHA1,只要shiro支持即可,加密次数加上比较好

4 把shiro的所有配置 配置给spring boot

package com.debug.config;

import java.util.*;

import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
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.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.shiro.mgt.SecurityManager;

import com.debug.shiro.MyCredentialsMatcher;
import com.debug.shiro.MyShiroRealm;

@Configuration
public class ShiroConfiguration {
	   @Bean(name="shiroFilter")
	    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager) {
	        ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
	        bean.setSecurityManager(manager);
	        //配置登录的url和登录成功的url
	        bean.setLoginUrl("/auth/toLogin");
	        bean.setSuccessUrl("/auth/index");
	        //配置访问权限
	        LinkedHashMap<String, String> filterChainDefinitionMap=new LinkedHashMap<String, String>();
	        filterChainDefinitionMap.put("/auth/toLogin", "anon"); //表示可以匿名访问
	        filterChainDefinitionMap.put("/auth/loginCheck", "anon"); //表示可以匿名访问
	        filterChainDefinitionMap.put("/auth/logout","logout");
	        filterChainDefinitionMap.put("/**", "authc");//表示需要认证才可以访问
	        
	        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
	        return bean;
	    }
	    //配置核心安全事务管理器
	    @Bean(name="securityManager")
	    public SecurityManager securityManager(@Qualifier("authRealm") MyShiroRealm authRealm) {
	        System.err.println("--------------shiro已经加载----------------");
	        DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
	        manager.setRealm(authRealm);
	        return manager;
	    }
	    //配置自定义的权限登录器
	    @Bean(name="authRealm")
	    public MyShiroRealm authRealm(@Qualifier("credentialsMatcher") CredentialsMatcher matcher) {
	    	MyShiroRealm authRealm=new MyShiroRealm();
	        authRealm.setCredentialsMatcher(matcher);
	        return authRealm;
	    }
	    //配置自定义的密码比较器
	    @Bean(name="credentialsMatcher")
	    public CredentialsMatcher credentialsMatcher() {
	        return new MyCredentialsMatcher();
	    }
	    @Bean
	    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
	        return new LifecycleBeanPostProcessor();
	    }
	    @Bean
	    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
	        DefaultAdvisorAutoProxyCreator creator=new DefaultAdvisorAutoProxyCreator();
	        creator.setProxyTargetClass(true);
	        return creator;
	    }
	    @Bean
	    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager manager) {
	        AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
	        advisor.setSecurityManager(manager);
	        return advisor;
	    }
}

到这里主要的代码就写好了,下面就是页面,controller了

5 编写controller和jsp

package com.debug.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;


@Controller
@RequestMapping("/auth")
public class AuthController {
	
   @RequestMapping("/toLogin")
   public String  toLogin() {
	  
	   return "login";
   }
   @RequestMapping("/loginCheck")
   public String  loginCheck(@RequestParam("userName")String userName,@RequestParam("password")String password) {
	   int flag=0;
	   //获取subject
       Subject currentUser = SecurityUtils.getSubject();
       UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
       token.setRememberMe(true);
       try {
          currentUser.login(token);
       }catch (AuthenticationException ae) {
    	   flag++;
    	  System.out.println("登录失败:"+ae.getMessage());
       }
       if(flag>0) {
    	   return "unauthorized";
       }else {
    	   return "index";
       }
	  
   }
   @RequestMapping("/index")
   public String  index() {
	  
	   return "index";
   }
   
   @RequestMapping("/unauthorized")
   public String  unauthorized() {
	   
	   return "unauthorized";
   }
   
   @RequestMapping("/user")
   public String  user() {
	   
	   return "user";
   }
   
   @RequestMapping("/admin")
   public String  admin() {
	   
	   return "admin";
   }
}

登录页面的jsp代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录页</title>
</head>
<body>
  <p>登录页</p>
  <form action="../auth/loginCheck" method="post">
          用户名:<input type="text" name="userName" /><br></br>
          密码:<input type="password" name="password" /><br></br>
          <input type="submit" value="提交"/>
   </form>
</body>
</html>

登录/授权成功后的页面index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>认证成功页</title>
</head>
<body>
  认证成功页<br><br>
  当前登录的用户是:<shiro:principal></shiro:principal><br><br>
  <shiro:hasPermission name="add">
    add功能<br><br>
  </shiro:hasPermission>
   <shiro:hasPermission name="delete">
   delete功能<br><br>
  </shiro:hasPermission>
   <shiro:hasPermission name="query">
   select功能<br><br>
  </shiro:hasPermission>
   <shiro:hasPermission name="update">
     update功能<br><br>
  </shiro:hasPermission>
  <a href="../auth/logout">登出</a>
</body>
</html>

其他页面随大家发挥,就不贴出来了,截图随后补上




注:

1 有关shiro里的session和web里的HttpSession大部分会有误解,它们并不冲突Controller里还是使用HttpSession,shiro的session则用在DAO或service层较好,session里的内容都是一样的,只是要注意适用范围

2 spring boot如果使用jsp作为视图解析器,要注意文件的存在位置,需要在src/main/下面建名为webapp的文件夹,里面放一层WEB-INF,下面才放jsp文件(夹)

##jsp##
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

这个配置不能错,否则容易出现404,如果是freemarker则直接在src/main下面建ftl的文件夹即可

3 为了在ftl中很好的使用shiro标签,还需要如下的配置

package com.debug.config;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import com.jagregory.shiro.freemarker.ShiroTags;

import freemarker.template.TemplateModelException;

@Component
public class ShiroTagsFreeMarkerCfg {
	@Autowired
	private FreeMarkerConfigurer freeMarkerConfigurer;
	@PostConstruct
	public void setSharedVariable() throws TemplateModelException {  
		freeMarkerConfigurer.getConfiguration().setSharedVariable("shiro", new ShiroTags());  
		freeMarkerConfigurer.getConfiguration().setNumberFormat("#");
	}  
}

以上就是分享内容了,下次打算把shiro的学习demo分享一下,持续学习中..........生气


版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/chenwill3/article/details/81006232
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-01 17:54:56
  • 阅读 ( 1328 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢