真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

詳解SpringBootSecurity

簡介

我們提供的服務有:網(wǎng)站設計、成都網(wǎng)站設計、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認證、雄縣ssl等。為上千企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務,是有科學管理、有技術(shù)的雄縣網(wǎng)站制作公司

Spring Security,這是一種基于 Spring AOP 和 Servlet 過濾器的安全框架。它提供全面的安全性解決方案,同時在 Web 請求級和方法調(diào)用級處理身份確認和授權(quán)。

工作流程

從網(wǎng)上找了一張Spring Security 的工作流程圖,如下。

詳解Spring Boot Security

圖中標記的MyXXX,就是我們項目中需要配置的。

快速上手

建表

表結(jié)構(gòu)

詳解Spring Boot Security

建表語句

DROP TABLE IF EXISTS `user`;
DROP TABLE IF EXISTS `role`;
DROP TABLE IF EXISTS `user_role`;
DROP TABLE IF EXISTS `role_permission`;
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `user` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
PRIMARY KEY (`id`) 
);
CREATE TABLE `role` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`) 
);
CREATE TABLE `user_role` (
`user_id` bigint(11) NOT NULL,
`role_id` bigint(11) NOT NULL
);
CREATE TABLE `role_permission` (
`role_id` bigint(11) NOT NULL,
`permission_id` bigint(11) NOT NULL
);
CREATE TABLE `permission` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`url` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`description` varchar(255) NULL,
`pid` bigint(11) NOT NULL,
PRIMARY KEY (`id`) 
);
INSERT INTO user (id, username, password) VALUES (1,'user','e10adc3949ba59abbe56e057f20f883e'); 
INSERT INTO user (id, username , password) VALUES (2,'admin','e10adc3949ba59abbe56e057f20f883e'); 
INSERT INTO role (id, name) VALUES (1,'USER');
INSERT INTO role (id, name) VALUES (2,'ADMIN');
INSERT INTO permission (id, url, name, pid) VALUES (1,'/user/common','common',0);
INSERT INTO permission (id, url, name, pid) VALUES (2,'/user/admin','admin',0);
INSERT INTO user_role (user_id, role_id) VALUES (1, 1);
INSERT INTO user_role (user_id, role_id) VALUES (2, 1);
INSERT INTO user_role (user_id, role_id) VALUES (2, 2);
INSERT INTO role_permission (role_id, permission_id) VALUES (1, 1);
INSERT INTO role_permission (role_id, permission_id) VALUES (2, 1);
INSERT INTO role_permission (role_id, permission_id) VALUES (2, 2);

pom.xml


 org.springframework.boot
 spring-boot-starter-security


 org.springframework.boot
 spring-boot-starter-thymeleaf


 org.springframework.boot
 spring-boot-starter-web


 org.thymeleaf.extras
 thymeleaf-extras-security4

User

public class User implements UserDetails , Serializable {
 private Long id;
 private String username;
 private String password;
 private List authorities;
 public Long getId() {
 return id;
 }
 public void setId(Long id) {
 this.id = id;
 }
 @Override
 public String getUsername() {
 return username;
 }
 public void setUsername(String username) {
 this.username = username;
 }
 @Override
 public String getPassword() {
 return password;
 }
 public void setPassword(String password) {
 this.password = password;
 }
 @Override
 public List getAuthorities() {
 return authorities;
 }
 public void setAuthorities(List authorities) {
 this.authorities = authorities;
 }
 /**
 * 用戶賬號是否過期
 */
 @Override
 public boolean isAccountNonExpired() {
 return true;
 }
 /**
 * 用戶賬號是否被鎖定
 */
 @Override
 public boolean isAccountNonLocked() {
 return true;
 }
 /**
 * 用戶密碼是否過期
 */
 @Override
 public boolean isCredentialsNonExpired() {
 return true;
 }
 /**
 * 用戶是否可用
 */
 @Override
 public boolean isEnabled() {
 return true;
 }
}

上面的 User 類實現(xiàn)了 UserDetails 接口,該接口是實現(xiàn)Spring Security 認證信息的核心接口。其中 getUsername 方法為 UserDetails 接口 的方法,這個方法返回 username,也可以是其他的用戶信息,例如手機號、郵箱等。getAuthorities() 方法返回的是該用戶設置的權(quán)限信息,在本實例中,從數(shù)據(jù)庫取出用戶的所有角色信息,權(quán)限信息也可以是用戶的其他信息,不一定是角色信息。另外需要讀取密碼,最后幾個方法一般情況下都返回 true,也可以根據(jù)自己的需求進行業(yè)務判斷。

Role

public class Role implements GrantedAuthority {
 private Long id;
 private String name;
 public Long getId() {
 return id;
 }
 public void setId(Long id) {
 this.id = id;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 @Override
 public String getAuthority() {
 return name;
 }
}

Role 類實現(xiàn)了 GrantedAuthority 接口,并重寫 getAuthority() 方法。權(quán)限點可以為任何字符串,不一定是非要用角色名。

所有的Authentication實現(xiàn)類都保存了一個GrantedAuthority列表,其表示用戶所具有的權(quán)限。GrantedAuthority是通過AuthenticationManager設置到Authentication對象中的,然后AccessDecisionManager將從Authentication中獲取用戶所具有的GrantedAuthority來鑒定用戶是否具有訪問對應資源的權(quán)限。

MyUserDetailsService

@Service
public class MyUserDetailsService implements UserDetailsService {
 @Autowired
 private UserMapper userMapper;
 @Autowired
 private RoleMapper roleMapper;
 @Override
 public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
 //查數(shù)據(jù)庫
 User user = userMapper.loadUserByUsername( userName );
 if (null != user) {
  List roles = roleMapper.getRolesByUserId( user.getId() );
  user.setAuthorities( roles );
 }
 return user;
 }
}

Service 層需要實現(xiàn) UserDetailsService 接口,該接口是根據(jù)用戶名獲取該用戶的所有信息, 包括用戶信息和權(quán)限點。

MyInvocationSecurityMetadataSourceService

@Component
public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {

 @Autowired
 private PermissionMapper permissionMapper;

 /**
 * 每一個資源所需要的角色 Collection決策器會用到
 */
 private static HashMap> map =null;


 /**
 * 返回請求的資源需要的角色
 */
 @Override
 public Collection getAttributes(Object o) throws IllegalArgumentException {
 if (null == map) {
  loadResourceDefine();
 }
 //object 中包含用戶請求的request 信息
 HttpServletRequest request = ((FilterInvocation) o).getHttpRequest();
 for (Iterator it = map.keySet().iterator() ; it.hasNext();) {
  String url = it.next();
  if (new AntPathRequestMatcher( url ).matches( request )) {
  return map.get( url );
  }
 }

 return null;
 }

 @Override
 public Collection getAllConfigAttributes() {
 return null;
 }

 @Override
 public boolean supports(Class<?> aClass) {
 return true;
 }

 /**
 * 初始化 所有資源 對應的角色
 */
 public void loadResourceDefine() {
 map = new HashMap<>(16);
 //權(quán)限資源 和 角色對應的表 也就是 角色權(quán)限 中間表
 List rolePermissons = permissionMapper.getRolePermissions();

 //某個資源 可以被哪些角色訪問
 for (RolePermisson rolePermisson : rolePermissons) {

  String url = rolePermisson.getUrl();
  String roleName = rolePermisson.getRoleName();
  ConfigAttribute role = new SecurityConfig(roleName);

  if(map.containsKey(url)){
  map.get(url).add(role);
  }else{
  List list = new ArrayList<>();
  list.add( role );
  map.put( url , list );
  }
 }
 }
}

MyInvocationSecurityMetadataSourceService 類實現(xiàn)了 FilterInvocationSecurityMetadataSource,F(xiàn)ilterInvocationSecurityMetadataSource 的作用是用來儲存請求與權(quán)限的對應關系。

FilterInvocationSecurityMetadataSource接口有3個方法:

boolean supports(Class<?> clazz):指示該類是否能夠為指定的方法調(diào)用或Web請求提供ConfigAttributes。

Collection

 getAllConfigAttributes():Spring容器啟動時自動調(diào)用, 一般把所有請求與權(quán)限的對應關系也要在這個方法里初始化, 保存在一個屬性變量里。

Collection

 getAttributes(Object object):當接收到一個http請求時, filterSecurityInterceptor會調(diào)用的方法. 參數(shù)object是一個包含url信息的HttpServletRequest實例. 這個方法要返回請求該url所需要的所有權(quán)限集合。

MyAccessDecisionManager

/**
 * 決策器
 */
@Component
public class MyAccessDecisionManager implements AccessDecisionManager {

 private final static Logger logger = LoggerFactory.getLogger(MyAccessDecisionManager.class);

 /**
 * 通過傳遞的參數(shù)來決定用戶是否有訪問對應受保護對象的權(quán)限
 *
 * @param authentication 包含了當前的用戶信息,包括擁有的權(quán)限。這里的權(quán)限來源就是前面登錄時UserDetailsService中設置的authorities。
 * @param object 就是FilterInvocation對象,可以得到request等web資源
 * @param configAttributes configAttributes是本次訪問需要的權(quán)限
 */
 @Override
 public void decide(Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
 if (null == configAttributes || 0 >= configAttributes.size()) {
  return;
 } else {
  String needRole;
  for(Iterator iter = configAttributes.iterator(); iter.hasNext(); ) {
  needRole = iter.next().getAttribute();

  for(GrantedAuthority ga : authentication.getAuthorities()) {
   if(needRole.trim().equals(ga.getAuthority().trim())) {
   return;
   }
  }
  }
  throw new AccessDeniedException("當前訪問沒有權(quán)限");
 }

 }

 /**
 * 表示此AccessDecisionManager是否能夠處理傳遞的ConfigAttribute呈現(xiàn)的授權(quán)請求
 */
 @Override
 public boolean supports(ConfigAttribute configAttribute) {
 return true;
 }

 /**
 * 表示當前AccessDecisionManager實現(xiàn)是否能夠為指定的安全對象(方法調(diào)用或Web請求)提供訪問控制決策
 */
 @Override
 public boolean supports(Class<?> aClass) {
 return true;
 }}

MyAccessDecisionManager 類實現(xiàn)了AccessDecisionManager接口,AccessDecisionManager是由AbstractSecurityInterceptor調(diào)用的,它負責鑒定用戶是否有訪問對應資源(方法或URL)的權(quán)限。

MyFilterSecurityInterceptor

@Component
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
 @Autowired
 private FilterInvocationSecurityMetadataSource securityMetadataSource;
 @Autowired
 public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
 super.setAccessDecisionManager(myAccessDecisionManager);
 }
 @Override
 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
 FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
 invoke(fi);
 }
 public void invoke(FilterInvocation fi) throws IOException, ServletException {
 InterceptorStatusToken token = super.beforeInvocation(fi);
 try {
  //執(zhí)行下一個攔截器
  fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
 } finally {
  super.afterInvocation(token, null);
 }
 }
 @Override
 public Class<?> getSecureObjectClass() {
 return FilterInvocation.class;
 }
 @Override
 public SecurityMetadataSource obtainSecurityMetadataSource() {
 return this.securityMetadataSource;
 }
}

每種受支持的安全對象類型(方法調(diào)用或Web請求)都有自己的攔截器類,它是AbstractSecurityInterceptor的子類,AbstractSecurityInterceptor 是一個實現(xiàn)了對受保護對象的訪問進行攔截的抽象類。

AbstractSecurityInterceptor的機制可以分為幾個步驟:

1. 查找與當前請求關聯(lián)的“配置屬性(簡單的理解就是權(quán)限)”
2. 將 安全對象(方法調(diào)用或Web請求)、當前身份驗證、配置屬性 提交給決策器(AccessDecisionManager)
3. (可選)更改調(diào)用所根據(jù)的身份驗證
4. 允許繼續(xù)進行安全對象調(diào)用(假設授予了訪問權(quán))
5. 在調(diào)用返回之后,如果配置了AfterInvocationManager。如果調(diào)用引發(fā)異常,則不會調(diào)用AfterInvocationManager。

AbstractSecurityInterceptor中的方法說明:

beforeInvocation()方法實現(xiàn)了對訪問受保護對象的權(quán)限校驗,內(nèi)部用到了AccessDecisionManager和AuthenticationManager;

finallyInvocation()方法用于實現(xiàn)受保護對象請求完畢后的一些清理工作,主要是如果在beforeInvocation()中改變了SecurityContext,則在finallyInvocation()中需要將其恢復為原來的SecurityContext,該方法的調(diào)用應當包含在子類請求受保護資源時的finally語句塊中。

afterInvocation()方法實現(xiàn)了對返回結(jié)果的處理,在注入了AfterInvocationManager的情況下默認會調(diào)用其decide()方法。
了解了AbstractSecurityInterceptor,就應該明白了,我們自定義MyFilterSecurityInterceptor就是想使用我們之前自定義的 AccessDecisionManager 和 securityMetadataSource。

SecurityConfig

@EnableWebSecurity注解以及WebSecurityConfigurerAdapter一起配合提供基于web的security。自定義類 繼承了WebSecurityConfigurerAdapter來重寫了一些方法來指定一些特定的Web安全設置。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 @Autowired
 private MyUserDetailsService userService;
 @Autowired
 public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
  //校驗用戶
  auth.userDetailsService( userService ).passwordEncoder( new PasswordEncoder() {
   //對密碼進行加密
   @Override
   public String encode(CharSequence charSequence) {
    System.out.println(charSequence.toString());
    return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
   }
   //對密碼進行判斷匹配
   @Override
   public boolean matches(CharSequence charSequence, String s) {
    String encode = DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
    boolean res = s.equals( encode );
    return res;
   }
  } );
 }
 @Override
 protected void configure(HttpSecurity http) throws Exception {
  http.authorizeRequests()
    .antMatchers("/","index","/login","/login-error","/401","/css/**","/js/**").permitAll()
    .anyRequest().authenticated()
    .and()
    .formLogin().loginPage( "/login" ).failureUrl( "/login-error" )
    .and()
    .exceptionHandling().accessDeniedPage( "/401" );
  http.logout().logoutSuccessUrl( "/" );
 }
}

MainController

@Controller
public class MainController {
 @RequestMapping("/")
 public String root() {
  return "redirect:/index";
 }
 @RequestMapping("/index")
 public String index() {
  return "index";
 }
 @RequestMapping("/login")
 public String login() {
  return "login";
 }
 @RequestMapping("/login-error")
 public String loginError(Model model) {
  model.addAttribute( "loginError" , true);
  return "login";
 }
 @GetMapping("/401")
 public String accessDenied() {
  return "401";
 }
 @GetMapping("/user/common")
 public String common() {
  return "user/common";
 }
 @GetMapping("/user/admin")
 public String admin() {
  return "user/admin";
 }
}

頁面

login.html





 
 首頁


 

page list

common page
admin page

index.html





 
 首頁


 

page list

common page
admin page

admin.html



 
 admin page


 success admin page?。?!



common.html



 
 common page


 success common page!?。?

401.html




 
 401 page


 

權(quán)限不夠

拒絕訪問!

最后運行項目,可以分別用 user、admin 賬號 去測試認證和授權(quán)是否正確。

總結(jié)

以上所述是小編給大家介紹的Spring Boot Security,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對創(chuàng)新互聯(lián)網(wǎng)站的支持!


新聞標題:詳解SpringBootSecurity
當前地址:http://weahome.cn/article/pdohej.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部