spring boot 中使用注解,bean实例不了,springboot会自动扫描启动文件所在包以及包一下文件注解,否则扫描不到。
spring boot security 集成文件结构:
表设计:
最基本5张表:
用户表:t_user;
资源表:t_resource
角色表:t_role
资源角色表:t_res_role
用户角色表:t_user_role
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>com.example</groupId>
<artifactId>springboot-security</artifactId>
<version>1</version>
<packaging>war</packaging>
<name>springboot-security</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</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-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</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-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.example.security;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.example.dao.RoleDao;
import com.example.dao.UserDao;
@Service
public class CustomUserService implements UserDetailsService{
@Autowired
UserDao userDao;
@Autowired
RoleDao roleDao;
@Override
public UserDetails loadUserByUsername(String username) {
Map<String,Object> user = userDao.queryByUserName(username);
if(user == null){
throw new UsernameNotFoundException("用户名不存在");
}
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
//用于添加用户的权限。只要把用户权限添加到authorities 就万事大吉。
List<Map<String,Object>>roles= roleDao.queryRoleByUId(user.get("id")+"");
for(Map<String,Object> role:roles)
{
authorities.add(new SimpleGrantedAuthority(role.get("rolId")+""));
}
return new org.springframework.security.core.userdetails.User(user.get("name")+"",
user.get("pwd")+"", authorities);
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public RoleDao getRoleDao() {
return roleDao;
}
public void setRoleDao(RoleDao roleDao) {
this.roleDao = roleDao;
}
}
package com.example.security;
import java.util.Collection;
import java.util.Iterator;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
@Service
public class MyAccessDecisionManager implements AccessDecisionManager{
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws org.springframework.security.access.AccessDeniedException, InsufficientAuthenticationException {
if(null== configAttributes || configAttributes.size() <=0) {
return;
}
ConfigAttribute c;
String needRole;
for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) {
c = iter.next();
needRole = c.getAttribute();
for(GrantedAuthority ga : authentication.getAuthorities()) {//authentication 为在注释1 中循环添加到 GrantedAuthority 对象中的权限信息集合
if(needRole.trim().equals(ga.getAuthority())) {
return;
}
}
}
throw new org.springframework.security.access.AccessDeniedException("no right");
}
@Override
public boolean supports(ConfigAttribute arg0) {
return true;
}
@Override
public boolean supports(Class<?> arg0) {
return true;
}
}
package com.example.security;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Service;
@Service
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
@Autowired
private FilterInvocationSecurityMetadataSource securityMetadataSource;
@Autowired
public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
super.setAccessDecisionManager(myAccessDecisionManager);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
//fi里面有一个被拦截的url
//里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
//再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
//执行下一个拦截器
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
@Override
public void destroy() {
}
@Override
public Class<?> getSecureObjectClass() {
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
}
package com.example.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Service;
import com.example.dao.ResourceDao;
import com.example.dao.RoleDao;
@Service
public class MyInvocationSecurityMetadataSourceService implements
FilterInvocationSecurityMetadataSource {
@Autowired
private ResourceDao resourceDao;
@Autowired
private RoleDao roleDao;
private HashMap<String, Collection<ConfigAttribute>> map =null;
/**
* 加载权限表中所有权限
*/
public void loadResourceDefine(){
map = new HashMap<>();
Collection<ConfigAttribute> array;
ConfigAttribute cfg;
List<Map<String,Object>> permissions = resourceDao.queryAll();
for(Map<String,Object> permission : permissions) {
array = new ArrayList<>();
List<Map<String,Object>>roles=roleDao.queryRoleByResId(permission.get("resId")+"");
for(Map<String,Object>role : roles){
cfg = new org.springframework.security.access.SecurityConfig(role.get("rolId")+"");
array.add(cfg);
}
//此处只添加了用户的名字,其实还可以添加更多权限的信息,例如请求方法到ConfigAttribute的集合中去。此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。
//用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,
map.put(permission.get("resUrl")+"", array);
}
}
//此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
if(map ==null) loadResourceDefine();
//object 中包含用户请求的request 信息
HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
AntPathRequestMatcher matcher;
String resUrl;
for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
resUrl = "/"+iter.next();
matcher=new AntPathRequestMatcher(resUrl);
String url=request.getRequestURI();
if(url.equals(resUrl)) {
return map.get(resUrl);
}
}
return null;
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
package com.example.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
UserDetailsService customUserService(){ //注册UserDetailsService 的bean
return new CustomUserService();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserService()); //user Details Service验证
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated() //任何请求,登录后可以访问
.and()
.formLogin()
// .loginPage("/login")//是否自定义登入页面 如果是自定义登入,登入页面中的action执行login,账户和密码使//username和password
.failureUrl("/login?error")
.permitAll() //登录页面用户任意访问
.and()
.logout().permitAll(); //注销行为任意访问
http.sessionManagement().maximumSessions(1).expiredUrl("/login");//是否限制用户人数
}
}
package com.example.security;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
/**
* @author 王峰
* @Time:2017年9月29日 上午10:35:18
* @version 1.0
* Function: TODO
*
* 密码处理
*/
@Service
public class Md5PasswordEncoderService implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return true;
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form name="logonform" method="post" id="loginForm" th:action="@{/login}">
<input type="text" class="usernameInput" name="username" id="j_username" tabindex="1" value="" title="请输入用户名">
<input type="password" class="passwordInput" name="password" id="j_password" tabindex="2" value="" title="请输入密码">
<input type="submit">
</form>
</body>
</html>
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
logging.level.com.com.example.dao=debug
logging.config=classpath:logging-config.xml
server.port=8081