序言:
据初步了解, 虽然spring security框架功能强大, 但Shiro框架相比于spring security框架更加简单, 功能也较齐全了, 是更多人用的安全框架。
注:这个项目是结合Mybatis做的, Mybatis相关配置看 Mybatis 的文章。
一、相关依赖:
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!--shiro与thymeleaf结合的依赖 -->
<!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
二、配置文件:
spring:
datasource:
username: root
password: admin
url: jdbc:mysql://localhost:3306/abc?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
# type: com.albaba.druid.pool.DruidDataSource
mybatis:
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper
三、Shiro配置文件:
ShiroConfig类:
package com.example.config;
import java.util.HashMap;
import java.util.Map;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/test/add", "perms[user:abc]");
filterMap.put("/test/update", "perms[user:add]");
filterMap.put("/test/**", "authc");
shiroFilterFactoryBean.setLoginUrl("/toLogin");
//设置自定义未授权页面
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorizedUrl");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
@Bean("securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
@Bean("userRealm")
public UserRealm getRealm(){
return new UserRealm();
}
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
UserRealm类:
package com.example.config;
import org.apache.shiro.SecurityUtils;
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.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import com.example.bean.User;
import com.example.service.UserService;
public class UserRealm extends AuthorizingRealm{
@Autowired
UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
System.out.println("执行授权逻辑");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Subject subject = SecurityUtils.getSubject();
System.out.println("subject.getPrincipal();" + subject.getPrincipal());
User user = (User) subject.getPrincipal();
User dbUser = userService.findById(user.getId());
System.out.println(user.getPerms());
info.addStringPermission(user.getPerms());
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
System.out.println("执行认证逻辑");
UsernamePasswordToken token = (UsernamePasswordToken) arg0;
User user = userService.findByuserName(token.getUsername());
if(user==null){
return null;
}
return new SimpleAuthenticationInfo(user, user.getPassword(), "");
}
}
@RequestMapping("/login")
public String login(String name, String password, Model model){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(name, password);
try{
subject.login(token);
return "success";
}
catch(UnknownAccountException e){
model.addAttribute("msg", "用户不存在");
return "forward:/toLogin";
}
catch(IncorrectCredentialsException e){
model.addAttribute("msg", "密码错误");
return "forward:/toLogin";
}
}
四、自定义验证登录页面:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>登陆页面</h1>
<font th:text="${msg}" style="color: red"></font>
<form name="f" action="login" method="post">
<br/>
用户名:
<input type="text" name="name" value="root" placeholder="name"><br/>
密码:
<input type="password" name="password" value="admin" placeholder="password"><br/>
<input type="checkbox" name="remember">记住我<br/>
<input name="submit" type="submit" value="提交">
</form>
</body>
</html>
五、Shiro与thymeleaf简单的权限标签:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1 th:text="${name}">name</h1>
<div shiro:hasPermission="user:abc">
进入用户添加功能:<a href="test/add">用户添加</a>
</div>
<div shiro:hasPermission="user:add">
进入用户更新功能:<a href="test/update">用户更新</a>
</div>
<div>
登录:<a href="tologin">login</a>
</div>
</body>
</html>
注:这里的 user:abc 我觉得大概只是代表了权限, 并不代表角色与权限, 角色与权限的关系是多对多的, 或许后面我会再更新一下
退出
Subject subject = SecurityUtils.getSubject();
if(subject.isAuthenticated())
subject.logout();