首先创建一个spring boot 项目(idea)
不多说,一路Next.
pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</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-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
</dependencies>
application.properties 文件
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=admin
#包别名
mybatis.typeAliasesPackage=com.shiro.pojo
#mapper文件路径
mybatis.mapperLocations=classpath:mapping/*.xml
#templates配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
此时项目已经可以启动了
第一步,编写shiro配置文件
package com.shiro.Config;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* description:
* author:fuLin.guo
* Date:2018/12/6 0006 10:22
*/
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
System.out.println("---------------------Shiro拦截器工厂类注入开始");
shiroFilterFactoryBean.setSecurityManager(securityManager);
//拦截器.
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/favicon.ico", "anon");
// 加入不需要拦截的路径, 比如登录接口
filterChainDefinitionMap.put("/toLogin", "anon");
//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
//<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
//<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterChainDefinitionMap.put("/**", "authc");
// 如果不设置默认会自动寻找Web工程根目录下的"/login.html"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/toerror");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 身份认证realm;
* (这个需要自己写,账号密码校验;权限等)
* @return
*/
@Bean
public MyShiroRealm myShiroRealm(){
MyShiroRealm myShiroRealm = new MyShiroRealm();
// TODO 测试,我没有开启加密规则
// myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); //设置加密规则
return myShiroRealm;
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置Realm
securityManager.setRealm(myShiroRealm());
// TODO 注入缓存管理器 后期使用redis
// securityManager.setCacheManager(ehCacheManager());//这个如果执行多次,也是同样的一个对象;
return securityManager;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor
= new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 凭证匹配器
* (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
* 所以我们需要修改下doGetAuthenticationInfo中的代码)
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5(""))
return hashedCredentialsMatcher;
}
}
第二部,编写自定义realm
package com.shiro.Config;
import com.shiro.pojo.UserInfo;
import com.shiro.service.UserInfoManager;
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 javax.annotation.Resource;
/**
* description:自定义权限匹配和账号密码匹配
* author:fuLin.guo
* Date:2018/12/6 0006 10:27
*/
public class MyShiroRealm extends AuthorizingRealm {
@Resource
private UserInfoManager userInfoManager;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
System.out.println("授权开始============");
String username =(String) principals.getPrimaryPrincipal();
System.out.println("授权用户名称:" + username);
// TODO 此处给所有路径权限
authorizationInfo.addRole("admin");
authorizationInfo.addStringPermission("/*");
/*List<RoleInfo> roleListByUserId = userInfoManager.getRoleListByUserId(user.getId());
for (RoleInfo role :roleListByUserId ){
authorizationInfo.addRole(role.getRolename());
*//* for (Permission permission :role.getPermissions()){
authorizationInfo.addStringPermission(permission.getPermission());
}*//*
}*/
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("开始身份认证:===》");
//获取用户的输入的账号.
UsernamePasswordToken token1 = (UsernamePasswordToken) token;
String user = token1.getUsername();
String pass = String.valueOf(token1.getPassword());
System.out.println("用户输入的账号密码===>>:"+user+","+pass);
//通过username从数据库中查找 User对象,如果找到,s没找到.
//实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
UserInfo userInfo = new UserInfo(); // 实际操作需要从数据库中获取用户对象
userInfo.setUsername("guo");
userInfo.setPassword("123");
if (null == userInfo) {
System.out.println("====> 用户找不到");
throw new UnknownAccountException();
}
// TODO 如果有停用或者离职等状态 继续判断
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
userInfo.getUsername(), //数据库中的用户名
userInfo.getPassword().toCharArray(), //数据库中的密吗,此处没有加盐
getName() //realm name
);
System.out.println("认证完毕==============");
return authenticationInfo;
}
}
第三步,创建登录接口
package com.shiro.controller;
import com.shiro.pojo.UserInfo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* description:
* author:fuLin.guo
* Date:2018/12/7 0007 10:09
*/
@Controller
@RequestMapping("/")
public class IndexController {
@RequiresPermissions(value = "/*")
@RequestMapping("/index")
public String index(){
System.out.println("go to index.......................");
return "index";
}
@RequestMapping("/toerror")
public String toerror(){
System.out.println("go to 403.......................");
return "error";
}
@RequestMapping("/login")
public String login(){
System.out.println("go to login.......................");
return "login";
}
@RequestMapping("/toLogin")
public String toLogin(UserInfo userInfo){
System.out.println("tologin...用户输入密码为:"+userInfo.getPassword());
//使用最常见的用户名密码的方式
AuthenticationToken token = new UsernamePasswordToken(
userInfo.getUsername(), userInfo.getPassword());
//获取Subject对象
Subject subject = SecurityUtils.getSubject();
System.out.println("登录验证开始.......");
try {
//传入上一步骤创建的token对象,登录,即进行身份验证操作。
subject.login(token);
} catch ( UnknownAccountException uae ) {
// TODO 实际情况不需要设置s ,采用全局异常拦截器
System.out.println("用户名未知");
} catch ( IncorrectCredentialsException ice ) {
System.out.println("凭据不正确,例如密码不正确 ...");
} catch ( LockedAccountException lae ) {
System.out.println("用户被锁定,例如管理员把某个用户禁用...");
} catch ( ExcessiveAttemptsException eae ) {
System.out.println("尝试认证次数多余系统指定次数 ...");
} catch ( AuthenticationException ae ) {
System.out.println("其他未指定异常");
}
//验证是否登录成功
if(subject.isAuthenticated()){
return "redirect:/index";
}else{
return "redirect:/login";
}
}
}
第四步,编写前端页面
第五步,启动
第一次, 访问http://localhost:8080/toLogin?username=guo&password=123234
密码写错
如果此时我不是用的打印方法,而是抛的异常等等..下面的就不会打印了
第二次,访问http://localhost:8080/toLogin?username=guo&password=123
密码正确
完成!
此时一个最简单基本的已经形成了.
spring boot 启动彩蛋!
在resources目录下创建banner.txt
然后复制以下代码保存即可
////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
////////////////////////////////////////////////////////////////////