springboot整合shiro

shiro能够说是简单却又功能强大的框架了,比起springSecurity 更加轻巧易用,在实际开发中用到也多,本人所在的公司也是用这个来作认证受权的,下面就介绍一下它的用法html

1,项目目录结构

2,pom.xml

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.1.RELEASE</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.0</version>
		</dependency>

		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.4.0</version>
		</dependency>
		<dependency>
		    <groupId>com.github.theborakompanioni</groupId>
		    <artifactId>thymeleaf-extras-shiro</artifactId>
		    <version>2.0.0</version>
		</dependency>
				
	</dependencies>

3,application.properties

spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.url=jdbc:mysql://localhost:3306/cloud2?useUnicode=true&characterEncoding=UTF8&useServerPrepStmts=true&prepStmtCacheSqlLimit=256&cachePrepStmts=true&prepStmtCacheSize=256&rewriteBatchedStatements=true

mybatis.config-location=classpath:mybatis-config.xml
mybatis.mapper-locations=classpath*:com/example/demo/**/dao/xml/*.xml

logging.level.com.example.demo=DEBUG


spring.thymeleaf.cache=false

4,mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
		<setting name="cacheEnabled" value="true" />
		<setting name="lazyLoadingEnabled" value="true" />
		<setting name="mapUnderscoreToCamelCase" value="true" />
		<setting name="jdbcTypeForNull" value="NULL" />
		<setting name="useActualParamName" value="false" />
	</settings>
</configuration>

5,配置路径映射

@Configuration
public class MvcConfig implements WebMvcConfigurer{

	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/").setViewName("index");
		registry.addViewController("/index").setViewName("index");
		registry.addViewController("/welcome").setViewName("welcome");
		registry.addViewController("/user").setViewName("user");
		registry.addViewController("/admin").setViewName("admin");
	}
}

6,shiro配置

①,shiro对特定路径的拦截

public class ShiroFilterMapFactory {

	public static Map<String, String> shiroFilterMap() {
//		设置路径映射,注意这里要用LinkedHashMap 保证有序
		LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
		filterChainDefinitionMap.put("/welcome", "anon");
		filterChainDefinitionMap.put("/login", "anon");
		filterChainDefinitionMap.put("/logout", "logout");
		filterChainDefinitionMap.put("/user", "roles[user]");
		filterChainDefinitionMap.put("/admin", "roles[admin]");
		filterChainDefinitionMap.put("/**", "user");//user容许 记住我和受权用户 访问,但在进行下单和支付时建议使用authc

		return filterChainDefinitionMap;
	}
}

②,shiro全局文件配置

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.example.demo.shiro.realm.ShiroRealm;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

@Configuration
public class ShiroConfig {

//	这是shiro的大管家,至关于mybatis里的SqlSessionFactoryBean
	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setLoginUrl("/login");
		shiroFilterFactoryBean.setSuccessUrl("/index");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(ShiroFilterMapFactory.shiroFilterMap());

		shiroFilterFactoryBean.setSecurityManager(securityManager);

		return shiroFilterFactoryBean;
	}
//  web应用管理配置
	@Bean
	public DefaultWebSecurityManager securityManager(Realm shiroRealm,CacheManager cacheManager,RememberMeManager manager) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setCacheManager(cacheManager);
		securityManager.setRememberMeManager(manager);//记住Cookie
		securityManager.setRealm(shiroRealm);
		return securityManager;
	}
//  加密算法
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		hashedCredentialsMatcher.setHashAlgorithmName("MD5");//采用MD5 进行加密
		hashedCredentialsMatcher.setHashIterations(1);//加密次数
		return hashedCredentialsMatcher;
	}
//	记住个人配置
	@Bean
	public RememberMeManager rememberMeManager() {
		Cookie cookie = new SimpleCookie("rememberMe");
        cookie.setHttpOnly(true);//经过js脚本将没法读取到cookie信息
        cookie.setMaxAge(60 * 60 * 24);//cookie保存一天
		CookieRememberMeManager manager=new CookieRememberMeManager();
		manager.setCookie(cookie);
		return manager;
	}
//  缓存配置
	@Bean
	public CacheManager cacheManager() {
		MemoryConstrainedCacheManager cacheManager=new MemoryConstrainedCacheManager();//使用内存缓存
		return cacheManager;
	}
//	配置realm,用于认证和受权
	@Bean
	public AuthorizingRealm shiroRealm(HashedCredentialsMatcher hashedCredentialsMatcher) {
		ShiroRealm shiroRealm = new ShiroRealm();
//		校验密码用到的算法
		shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
		return shiroRealm;
	}
//	启用shiro方言,这样能在页面上使用shiro标签
	@Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
/**
     * 启用shiro注解
     *
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

③,shiroRealm的编写

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.IncorrectCredentialsException;
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 org.springframework.beans.factory.annotation.Autowired;

import com.example.demo.user.bean.User;
import com.example.demo.user.dao.UserDao;
//继承AuthorizingRealm,重写认证和受权方法
public class ShiroRealm extends AuthorizingRealm {
	
	@Autowired
	private UserDao userDao;

	/**
	 * 受权方法,若是不设置缓存管理的话,须要访问须要必定的权限或角色的请求时会进入这个方法
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		User principal = (User) principals.getPrimaryPrincipal();
		Set<String> roles = new HashSet<>();
		roles.add("user");
		if("admin".equals(principal.getUsername())){
			roles.add("admin");
		}
		
		return new SimpleAuthorizationInfo(roles);
	}

	/**
	 * 认证方法
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		
		UsernamePasswordToken userToken=(UsernamePasswordToken) token;
		//根据登陆名查询用户,这里不用校验密码,由于密码的校验是交给shiro来完成的
		User userInfo=userDao.findByUserName(userToken.getUsername());
		
		if(userInfo == null) {
			throw new IncorrectCredentialsException("用户名或密码不正确");
		}
		
		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
	                userInfo,//用户
	                userInfo.getPassword(),//密码
	                ByteSource.Util.bytes(userInfo.getUsername()),//盐值用 ByteSource.Util.bytes 来生成
	                getName()//realm name
	        );
		return authenticationInfo;
	}
    public static void main(String[] args) {
		//算出盐值
		String credentials="123";
		String salt="小苏";
		String hashAlgorithmName="MD5";
		int hashIterations=1024;
		SimpleHash simpleHash = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
		System.out.println(simpleHash);
		
	}
	
}

④,LoginController 的编写

import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.example.demo.user.bean.User;

@Controller
public class LoginController {

	private static Logger logger=LoggerFactory.getLogger(LoginController.class);
	
	@PostMapping("login")
	public ModelAndView login(User user,RedirectAttributes redirectAttributes,boolean rememberMe) {
		ModelAndView view =new ModelAndView();
		 String userName = user.getUsername();
		 Subject currentUser = SecurityUtils.getSubject();
		 if(!currentUser.isAuthenticated()) {
			 UsernamePasswordToken token =new UsernamePasswordToken(userName,user.getPassword());
			 try {
				 if(rememberMe) {
					 token.setRememberMe(true);
				 }
				 currentUser.login(token);
				 
				 
				 view.setViewName("redirect:/");
			 }catch (UnknownAccountException uae) {
		            logger.info("对用户[" + userName + "]进行登陆验证..验证未经过,未知帐户");
		            redirectAttributes.addFlashAttribute("message", "未知帐户");
		        } catch (IncorrectCredentialsException ice) {
		            logger.info("对用户[" + userName + "]进行登陆验证..验证未经过,错误的凭证");
		            redirectAttributes.addFlashAttribute("message", "用户名或密码不正确");
		        } catch (LockedAccountException lae) {
		            logger.info("对用户[" + userName + "]进行登陆验证..验证未经过,帐户已锁定");
		            redirectAttributes.addFlashAttribute("message", "帐户已锁定");
		        } catch (ExcessiveAttemptsException eae) {
		            logger.info("对用户[" + userName + "]进行登陆验证..验证未经过,错误次数过多");
		            redirectAttributes.addFlashAttribute("message", "用户名或密码错误次数过多");
		        } catch (AuthenticationException ae) {
		            //经过处理Shiro的运行时AuthenticationException就能够控制用户登陆失败或密码错误时的情景
		            logger.info("对用户[" + userName + "]进行登陆验证..验证未经过,堆栈轨迹以下");
		            ae.printStackTrace();
		            redirectAttributes.addFlashAttribute("message", "用户名或密码不正确");
		        }
		 }
		 
		 view.setViewName("redirect:/login");
		 return view;
		 
	}
	
	@GetMapping("/login")
    public String login(HttpServletRequest request) {
        try {
            if ((null != SecurityUtils.getSubject() && SecurityUtils.getSubject().isAuthenticated()) || SecurityUtils.getSubject().isRemembered()) {
                return "redirect:/";
            } else {
            	logger.info("--进行登陆验证..验证开始");
                return "login";
            }
        } catch (Exception e) {
        		e.printStackTrace();
        }
        return "login";
    }
@RequestMapping("su")
	public ModelAndView su() {
		ModelAndView view =new ModelAndView("index");
		System.out.println("来到了su方法。。。");
		
		return view;
	}
	

	@RequiresRoles({"admin"})
	@RequestMapping("xiao")
	public ModelAndView xiao() {
		ModelAndView view =new ModelAndView("index");
		System.out.println("来到了xiao方法。。。");
		
		return view;
	}
}

7,登陆的实体

import java.io.Serializable;

public class User implements Serializable{

	private String username;
	private String password;
	private String salt;
// get/set
}

8,dao层实现

①,UserDao 代码

import org.apache.ibatis.annotations.Mapper;

import com.example.demo.user.bean.User;

@Mapper
public interface UserDao {
	
	User findByUserName(String username);

}

②,UserMapper.xml 代码

<?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.example.demo.user.dao.UserDao">

	<select id="findByUserName" resultType="com.example.demo.user.bean.User">
		select * from user where username=#{username}
	</select>

</mapper>

9,启动类代码

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

//开启AspectJAutoProxy
@EnableAspectJAutoProxy
@SpringBootApplication
public class SpringbootShiroApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringbootShiroApplication.class, args);
	}
}

10,测试页面

①,login.html

<html>
<head>
<meta charset="UTF-8"/>
<title>login</title>
</head>
<body>

<form action="/login" method="post">
	用户名:<input type="text" name="username" /><br/>
	密码:<input type="password" name="password" /><br/>
	<label>
		<input type="checkbox" name="rememberMe" />记住我
	</label><br/>
	
	<input type="submit" value="submit" />
</form>

</body>
</html>

②,index.html

<html xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8"/>
<title>login</title>
</head>
<body>
<shiro:guest>    
	<a th:href="@{/login}">登陆</a> <a>注册</a>  
</shiro:guest>    

<shiro:user>    
	欢迎<shiro:principal property="username"/>  &nbsp;
	<a th:href="@{/logout}">退出</a>
</shiro:user>  


index page..

<shiro:hasRole="user">
	有用户角色哦、、、、
</shiro:hasRole>
<shiro:hasRole="admin">
	有admin角色哦、、、、
</shiro:hasRole>
<br>
<a th:href="@{/su}">苏方法</a>

<br>
<a th:href="@{/xiao}">小方法</a>

<br/>

<div shiro:hasRole="user">
	===有用户角色哦、、、、
</div>
<br/>
<shiro:authenticated>    
	w jinguo renzl...
</shiro:authenticated> 


<br/>
<div shiro:hasRole="admin">
	===有admin角色哦、、、、
</div>
<div shiro:lacksRole="admin">
	===mei有admin角色哦、、、、
</div>

<br/>

<script th:src="@{/js/jquery-2.2.3.min.js}"></script>
<script>

$(function(){

	console.log('<shiro:principal property="username"/>');
})
	
</script>
</body>
</html>

以上为shiro标签的使用,比较简单,至于shiro有那些能使用的标签,能够参考,Shiro-Dialect.xml java

③,user.html

<html>
<head>
<meta charset="UTF-8"/>
<title>login</title>
</head>
<body>

user page..

</body>
</html>

admin.html和welcome.html 与其类似,这里就不写了mysql

④,测试

最后便能进行登陆测试,很简单吧,快来试试吧,体验shiro的强大功能jquery

11,彩蛋

①,shiro对密码校验原理

1,由于UsernamePasswordToken 保存着表单信息,全部咱们能够在其getPassword 方法打一个断点git

2,进行登陆,走到了这个断点,往前面观察,就是在这里进行密码的比对github

3,在return equals 所在行打一个断点,其中表单登陆密码通过加密后获得结果保存在tokenHashedCredentials中。web

4,accountCredentials 为咱们认证时放入的从数据库读取的密码算法

5,而后进行比对spring

相关文章
相关标签/搜索