springboot +spring security4 +thymeleaf 后台管理系统
需求:一个后台管理系统,现在用的springboot 微框架比较多, 所以这里也使用了, 后台权限用 spring security ,之前以前觉得听复杂 。 后来发现还是蛮简单的, 看了源代码之后。模板用的 thymeleaf, 以上是背景介绍。
看看实现吧。
1.核心类(安全类)
package com.ycmedia.security; import org.springframework.beans.factory.annotation.Autowired; 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.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Autowired private YcAnthencationProder provider; @Override protected void configure(HttpSecurity http) throws Exception { //允许访问静态资源 http.authorizeRequests() .antMatchers("/upload", "/css/**", "/js/**", "/images/**", "/resources/**", "/lib/**", "/skin/**", "/template/**") .permitAll(); //所有的访问都需要权限验证 http.authorizeRequests().anyRequest().authenticated(); //访问失败页url http.formLogin().failureUrl("/login?error"). //登录信息保存 successHandler(loginSuccessHandler()). //访问成功页url defaultSuccessUrl("/login") //默认访问页 .loginPage("/login") .permitAll().and().logout() .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) //注销失败跳转到登录页面 .logoutSuccessUrl("/login").permitAll(); // 允许iframe 嵌套 http.headers().frameOptions().disable(); //关闭csrf 防止循环定向 http.csrf().disable(); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/resources/**"); web.ignoring().antMatchers("/webjars/**"); web.ignoring().antMatchers("/img/**"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //采用自定义验证 auth.authenticationProvider(provider); //需要采用加密 // auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(4); } /** * 用户或者管理员登录日志 */ @Bean public LoginSuccessHandler loginSuccessHandler(){ return new LoginSuccessHandler(); } }
======================这些配置其实很简单, 很强大, 网上的例子很多。
2、启动类 ,因为比较懒
package com.ycmedia; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; import org.springframework.boot.context.embedded.ErrorPage; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpStatus; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import com.ycmedia.service.UserService; @SpringBootApplication @EnableAutoConfiguration public class Application extends WebMvcConfigurerAdapter { @Bean public UserDetailsService userDetailsService() { return new UserService(); } /** * 自定义异常页 */ @Bean public EmbeddedServletContainerCustomizer containerCustomizer() { return (container -> { ErrorPage error401Page = new ErrorPage(HttpStatus.FORBIDDEN, "/403.html"); ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/404.html"); ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500.html"); container.addErrorPages(error401Page, error404Page, error500Page); }); } @Override public void addViewControllers(ViewControllerRegistry registry) { } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
这个启动类, 加载了 核心实现 userDetailService。
3、实现类重写。
package com.ycmedia.service; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.annotation.security.RolesAllowed; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; 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.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import com.ycmedia.dao.UserDao; import com.ycmedia.entity.User; @Service public class UserService implements UserDetailsService { @Autowired private UserDao userDao; public List<User> loadAllUsers() { return userDao.loadAllUsers(); } /** * 保存用户 * * @param user */ public void save(User user) { BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); if (user.getRole() == "0") { user.setRole("ROLE_ADMIN"); } else { user.setRole("ROLE_USER"); } user.setPassword(passwordEncoder.encode(user.getPassword())); userDao.insert(user); } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userDao.findUserByName(username); if (user == null) { throw new UsernameNotFoundException(username + " not found"); } System.err.println(user.getRole() + "正在执行查询角色名称"); return new UserDetails() { private static final long serialVersionUID = 3720901165271071386L; @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<SimpleGrantedAuthority> auths = new ArrayList<>(); auths.add(new SimpleGrantedAuthority(user.getRole())); return auths; } @Override public String getPassword() { return user.getPassword(); } @Override public String getUsername() { return username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }; } public User getUserByname(String username) { return userDao.findUserByName(username); } }
spring security 会自动调用被重写的 loaduserbyusername ,获取 用户所有的信息 存在 Authentication
4.看下controller 的实现, 是基于注解的,因为项目比较简单。
/**tpl-add * 跳转到编辑广告 * @return */ @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')") @RequestMapping(value = "/product-edit") public ModelAndView toEditAd(@RequestParam("id")String id ,Creative creative){ creative = adservice.findAdById(id); return new ModelAndView("product-add" ,"creative",creative ); } /** * * 跳转到编辑广告 * @return */ @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')") @RequestMapping(value = "/tpl-add") public ModelAndView toTplAdd(@ModelAttribute(value="tpl")CreativeTpl tpl){ return new ModelAndView("tpl-add" ); }
在方法 是作用注解, 普通用户 没有权限, 直接跳到403 页面。
5、实体 User。 这个很简单。
package com.ycmedia.entity; public class User { private Integer id; private String username; private String password; private String role; private String realname; private String mobile; private Integer state; private String info; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } public String getRealname() { return realname; } public void setRealname(String realname) { this.realname = realname; } public String getMobile() { return mobile; } public void setMobile(String mobile) { this.mobile = mobile; } public Integer getState() { return state; } public void setState(Integer state) { this.state = state; } public String getInfo() { return info; } public void setInfo(String info) { this.info = info; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } }
7 。 表的话就用了一张。 user表
用户对应的权限和用户在一张表。
==========================================================================================================================================
看看源码===========
==========================这里包括用户名,密码校验等。
spring真的很强大
=======
附上 gitHup 完整项目地址:https://github.com/zhuliangxing823/quanming.git