1.pom.xml
org.springframework.boot
spring-boot-starter-parent
1.5.9.RELEASE
org.apache.shiro
shiro-spring
1.3.2
com.github.theborakompanioni
thymeleaf-extras-shiro
1.2.1
2.config
package com.xlt.xfzb.web.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.xlt.xfzb.web.security.UserRealm;
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.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* @Classname ShiroConfiguration
* @Description 权限控制设置
* @Date 2020/3/27 17:47
* @Created by xm
*/
@Configuration
public class ShiroConfiguration {
// 创建自定义 realm
@Bean
public UserRealm userRealm() {
UserRealm userRealm = new UserRealm();
return userRealm;
}
// 创建 SecurityManager 对象
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm());
return securityManager;
}
// Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
/**
* anon:匿名用户可访问
* authc:认证用户可访问
* user:使用rememberMe可访问
* perms:对应权限可访问
* role:对应角色权限可访问
*/
Map map = new HashMap<>();
// 开放登录接口
map.put("/login", "anon");
map.put("/error", "anon");
map.put("/img/**","anon");
map.put("/css/**","anon");
map.put("/lib/**","anon");
map.put("/js/**","anon");
// 对所有用户认证
map.put("/**", "authc");
// 登出
map.put("/logout", "logout");
// 登录
// 注意:这里配置的 /login 是指到 @RequestMapping(value="/login")中的 /login
shiroFilterFactoryBean.setLoginUrl("/login");
// 首页
shiroFilterFactoryBean.setSuccessUrl("/index");
// 错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/error/unAuth");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
// 加入注解的使用,不加这个,注解不生效
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
// 跟上面的注解配置搭配使用,有时候加了上面的配置后注解不生效,需要加入下面的配置
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator app = new DefaultAdvisorAutoProxyCreator();
app.setProxyTargetClass(true);
return app;
}
//页面标签对象
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
3.依赖实体
用户
package com.xlt.xfzb.web.entity;
import java.util.HashSet;
import java.util.Set;
/**
* @Classname User
* @Description 权限测试用户
* @Date 2020/3/27 17:44
* @Created by xm
*/
public class UserEntity {
private String id;
private String name;
private String password;
//账号
private String account;
//性别
private String sex;
//部门id
private String deptId;
//工号
private String work;
//自定义集合
private Set roles = new HashSet();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set getRoles() {
return roles;
}
public void setRoles(Set roles) {
this.roles = roles;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getDeptId() {
return deptId;
}
public void setDeptId(String deptId) {
this.deptId = deptId;
}
public String getWork() {
return work;
}
public void setWork(String work) {
this.work = work;
}
}
角色
package com.xlt.xfzb.web.entity;
import java.util.HashSet;
import java.util.Set;
/**
* @Classname Role
* @Description TODO
* @Date 2020/3/30 11:42
* @Created by xm
*/
public class RoleEntity {
private String id;
//姓名
private String name;
//备注
private String remark;
//自定义菜单实体
private Set permissions = new HashSet<>();//一个角色有多个权限
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set getPermissions() {
return permissions;
}
public void setPermissions(Set permissions) {
this.permissions = permissions;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}
权限
package com.xlt.xfzb.web.entity;
/**
* @Classname Permission
* @Description 权限实体
* @Date 2020/3/30 11:41
* @Created by xm
*/
public class PermissionEntity {
private String id;
private String name;
//路径
private String url;
//描述
private String descrirtion;
//父id
private String pid;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDescrirtion() {
return descrirtion;
}
public void setDescrirtion(String descrirtion) {
this.descrirtion = descrirtion;
}
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
}
4.自定义异常
package com.xlt.xfzb.web.security;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @Classname NoPermissionException
* @Description TODO
* @Date 2020/3/31 11:16
* @Created by xm
*/
@ControllerAdvice
public class NoPermissionException {
// 授权失败,就是说没有该权限
@ExceptionHandler(UnauthorizedException.class)
public String handleShiroException(Exception ex) {
return "/error/unAuth";
}
@ResponseBody
@ExceptionHandler(AuthorizationException.class)
public String AuthorizationException(Exception ex) {
return "权限认证失败";
}
}
5.权限认证
package com.xlt.xfzb.web.security;
import com.netflix.discovery.provider.Serializer;
import com.xlt.xfzb.web.entity.PermissionEntity;
import com.xlt.xfzb.web.entity.RoleEntity;
import com.xlt.xfzb.web.entity.UserEntity;
import com.xlt.xfzb.web.service.UserService;
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.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* @Classname UserRealm
* @Description TODO
* @Date 2020/3/31 11:02
* @Created by xm
*/
@Service
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 用户授权
**/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principalCollection) {
System.out.println("===执行授权===");
Subject subject = SecurityUtils.getSubject();
UserEntity user = (UserEntity)subject.getPrincipal();
if(user != null){
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 角色字符串集合
Collection rolesCollection = new HashSet<>();
// 权限字符串集合
Collection premissionCollection = new HashSet<>();
// 读取并赋值用户角色与权限
Set roles = user.getRoles();
for(RoleEntity role : roles){
rolesCollection.add(role.getName());
Set permissions = role.getPermissions();
for (PermissionEntity permission : permissions){
// 权限名称为PermissionEntity为字段url
premissionCollection.add(permission.getUrl());
}
info.addStringPermissions(premissionCollection);
}
info.addRoles(rolesCollection);
return info;
}
return null;
}
/**
* 用户认证
**/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("===执行认证===");
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
UserEntity bean = userService.findByName(token.getUsername());
if(bean == null){
// 用户不存在
throw new UnknownAccountException();
} else {
bean = userService.findById(bean.getId());
if(null == bean) {
// 认证失败
throw new AuthenticationException();
}
}
ByteSource credentialsSalt = ByteSource.Util.bytes(bean.getName());
return new SimpleAuthenticationInfo(bean, bean.getPassword(),
credentialsSalt, getName());
}
}
6.持久层
package com.xlt.xfzb.web.mapper;
import com.xlt.xfzb.web.entity.UserEntity;
import org.springframework.stereotype.Service;
/**
* @Classname UserMapper
* @Description 用户测试
* @Date 2020/3/31 10:45
* @Created by xm
*/
@Service
public interface UserMapper {
// 根据用户名称,查询用户信息
UserEntity findByName(String name);
// 根据用户id,查询用户信息、角色、权限
UserEntity findById(String id);
}
7.service
package com.xlt.xfzb.web.service;
import com.xlt.xfzb.web.entity.UserEntity;
/**
* @Classname UserService
* @Description 用户相关业务接口
* @Date 2020/3/30 11:48
* @Created by xm
*/
public interface UserService {
/**
* 根据用户名称查询用户信息
* @param name
* @return
*/
UserEntity findByName(String name);
/**
* 根据用户ID查询用户角色及权限
* @param id
* @return
*/
UserEntity findById(String id);
}
package com.xlt.xfzb.web.service.impl;
import com.xlt.xfzb.web.entity.UserEntity;
import com.xlt.xfzb.web.mapper.UserMapper;
import com.xlt.xfzb.web.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Classname UserService
* @Description 用户相关业务接口实现
* @Date 2020/3/30 11:48
* @Created by xm
*/
@Service
public class UserServiceImpl implements UserService {
//测试用户持久层
@Autowired
private UserMapper userMapper;
/**
* 根据用户名称查询用户信息
* @param name
* @return
*/
@Override
public UserEntity findByName(String name) {
return userMapper.findByName(name);
}
/**
* 根据用户ID查询用户角色及权限
* @param id
* @return
*/
@Override
public UserEntity findById(String id) {
return userMapper.findById(id);
}
}
8.controller
package com.xlt.xfzb.web.controller;
import com.xlt.xfzb.web.entity.UserEntity;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
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.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Classname MainController
* @Description 主页验证前端控制器
* @Date 2020/3/31 11:18
* @Created by xm
*/
@Controller
public class MainController {
private static final Logger log=LoggerFactory.getLogger(MainController.class);
/**
* 主页跳转
* @param model 携带当前登录用户信息返回
* @return
*/
@RequestMapping("/index")
public String index(Model model){
UserEntity user = (UserEntity) SecurityUtils.getSubject().getPrincipal();
model.addAttribute("uid",user.getId());
model.addAttribute("uname",user.getName());
log.info("当前登录用户-----"+user.getName());
return "index";
}
/**
* 登录接口+登录页面
* @param request
* @param response
* @return
*/
@RequestMapping("/login")
public String login(HttpServletRequest request, HttpServletResponse response){
response.setHeader("root", request.getContextPath());
String userName = request.getParameter("username");
String password = request.getParameter("password");
// 等于null说明用户没有登录,只是拦截所有请求到这里,那就直接让用户去登录页面,就不认证了。
// 如果这里不处理,那个会返回用户名不存在,逻辑上不合理,用户还没登录怎么就用户名不存在?
if(null == userName) {
return "login";
}
// 1.获取Subject
Subject subject = SecurityUtils.getSubject();
// 2.封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
// 3.执行登录方法
try{
subject.login(token);
return "redirect:/index";
} catch (UnknownAccountException e){
// 这里是捕获自定义Realm的用户名不存在异常
log.info("用户名不存在!");
} catch (IncorrectCredentialsException e){
log.info("密码错误!");
} catch (AuthenticationException e) {
log.info("认证失败!");
}
return "login";
}
/**
* 用户登出操作
* @return
*/
@RequestMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
if (subject != null) {
subject.logout();
}
return "login";
}
/**
* 错误页面
* @return
*/
@RequestMapping("/error/unAuth")
public String unAuth(){
return "/error/unAuth";
}
@RequestMapping("/err")
public String err(){
return "/error/unAuth";
}
}
9.登录页面 login.html
登录
用户登录
10.首页 index.HTML
首页
首页
11.数据库结构
user
role
user_role
permission
role_permission
12. 页面测试