赞
踩
集成spring security实现登录权限资源控制
项目结构,标准的springboot项目结构
配置pom文件引入相关jar
<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.zzp</groupId> <artifactId>my-spring-security</artifactId> <version>0.0.1-SNAPSHOT</version> <name>tomato-study</name> <description>学习spring security</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </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.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.mariadb.jdbc</groupId> <artifactId>mariadb-java-client</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
application.yml配置
server: port: 8088 spring: thymeleaf: cache: false datasource: url: jdbc:mysql://localhost:3306/spring-security?useUnicode=true&characterEncoding=utf8mb4 driver-class-name: org.mariadb.jdbc.Driver username: root password: 123456 jpa: hibernate: ddl-auto: update database-platform: org.hibernate.dialect.MySQL5Dialect logging: file: spring.log level: root: INFO
配置security 新建一个类WebSecurityConfig集成 WebSecurityConfigurerAdapter
package com.zzp.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; 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.User; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import com.zzp.authentication.MyAuthenticationFailHandler; import com.zzp.authentication.MyAuthenticationSuccessHandler; @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ /** * 匹配 "/" 路径,不需要权限即可访问 * 匹配 "/user" 及其以下所有路径,都需要 "USER" 权限 * 登录地址为 "/login",登录成功默认跳转到页面 "/user" * 退出登录的地址为 "/logout",退出成功后跳转到页面 "/login" * 默认启用 CSRF这是跨域设置 */ /** * 注入 自定义的 登录成功处理类 */ @Autowired private MyAuthenticationSuccessHandler mySuccessHandler; @Autowired private MyAuthenticationFailHandler myFailHandler; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/").permitAll() .antMatchers("/user/**").hasAuthority("USER") .antMatchers("/login").permitAll() .antMatchers("/logout").permitAll() .and() .formLogin() .loginPage("/login") .loginProcessingUrl("/loginUser") .successForwardUrl("/user") .successHandler(mySuccessHandler) .failureHandler(myFailHandler) .and() .logout().logoutUrl("/logout").logoutSuccessUrl("/login"); // .and() // .csrf().disable(); //这里不用自定义的认证过滤 //http.addFilterAt(customFromLoginFilter(), UsernamePasswordAuthenticationFilter.class); } /** * 自定义认证过滤器 */ private CustomFromLoginFilter customFromLoginFilter() { return new CustomFromLoginFilter("/login"); } }
新建两个登录成功和失败处理类
package com.zzp.authentication; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.stereotype.Component; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 自定义登录失败处理器 * Created by Fant.J. */ @Component("myAuthenctiationFailureHandler") public class MyAuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler { /** * 日志 */ private Logger logger = LoggerFactory.getLogger(getClass()); public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { logger.info("登录失败"); System.out.println("----HHH---->>"+e.getMessage()); super.setDefaultFailureUrl("/login?error=true"); super.onAuthenticationFailure(request,response,e); } }
package com.zzp.authentication; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.web.DefaultRedirectStrategy; import org.springframework.security.web.RedirectStrategy; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 自定义登录成功处理类 * Created by Fant.J. */ @Component("myAuthenctiationSuccessHandler") public class MyAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { /** * 日志 */ private Logger logger = LoggerFactory.getLogger(getClass()); // private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { logger.info("登录成功"); super.setDefaultTargetUrl("/user"); super.onAuthenticationSuccess(request, response, authentication); //redirectStrategy.sendRedirect(request, response, "/user"); } }
新增一个类实现接口UserDetailsService,这个是登录认证处理相关的。
package com.zzp.config; import com.zzp.entity.UserDO; import com.zzp.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; 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.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; @Service public class DbUserDetailsService implements UserDetailsService { private final UserService userService; /** * 重写PasswordEncoder 接口中的方法,实例化加密策略 * @return 返回 BCrypt 加密策略 */ @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Autowired private PasswordEncoder myPasswordEncoder; @Autowired DbUserDetailsService(UserService userService){ this.userService = userService; } public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { UserDO userDO = userService.getByUsername(username); if (userDO == null){ System.out.println("=========用户不存在!=========>>"); throw new UsernameNotFoundException("用户不存在!"); } List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<SimpleGrantedAuthority>(); simpleGrantedAuthorities.add(new SimpleGrantedAuthority("USER")); String password = userDO.getPassword(); password = myPasswordEncoder.encode(password); System.out.println("=========loadUserByUsername=========>>"); return new org.springframework.security.core.userdetails.User(userDO.getUsername(), password, simpleGrantedAuthorities); } }
Controller编写
homeController
@Controller
public class HomeController {
@GetMapping({"/", "/index", "/home"})
public String root(){
return "index";
}
@GetMapping("/login")
public String login(){
return "login";
}
}
UserController
@Controller
public class UserController {
@RequestMapping("/user")
public String user(@AuthenticationPrincipal Principal principal, Model model){
System.out.println("========user=======>>");
model.addAttribute("username", principal.getName());
return "user/user";
}
}
user实体类
package com.zzp.entity; import javax.persistence.*; @Entity @Table(name = "user") public class UserDO { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; /** * 账号 */ private String username; /** * 密码 */ private String password; /** * 昵称 */ private String nickname; public Long getId() { return id; } public void setId(Long id) { this.id = id; } 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 getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } }
Repository 就是dao 继承CrudRepository
package com.zzp.repository;
import com.zzp.entity.UserDO;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends CrudRepository<UserDO, Long> {
UserDO findByUsername(String username);
}
service
public interface UserService { /** * 添加新用户 * * username 唯一, 默认 USER 权限 */ void insert(UserDO userDO); /** * 查询用户信息 * @param username 账号 * @return UserEntity */ UserDO getByUsername(String username); } @Service @Primary @Slf4j public class BaseUserService implements UserService { private final UserRepository userRepository; public BaseUserService(UserRepository userRepository){ this.userRepository = userRepository; } public void insert(UserDO userDO) { String username = userDO.getUsername(); if (exist(username)){ throw new RuntimeException("用户名已存在!"); } userRepository.save(userDO); } public UserDO getByUsername(String username) { return userRepository.findByUsername(username); } /** * 判断用户是否存在 */ private boolean exist(String username){ UserDO userDO = userRepository.findByUsername(username); return (userDO != null); } }
前端页面
user.html
<html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>用户中心 | Spring Security Demos</title> <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> </head> <body style="background-color: #f1f1f1; padding-bottom: 0"> <div th:insert="~{header :: nav}"></div> <div class="container" style="margin-top: 60px"> <div style="text-align: center; margin-top: 10%"> <img src="http://upload.jianshu.io/users/upload_avatars/3424642/fb55f16faaf6.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240" alt="avatar" class="img-circle" style="margin: 0 auto"> <p th:text="${username}" style="margin-top: 25px; font-size: 20; color: crimson">zzp</p> <form th:action="@{/logout}" method="post"> <button class="btn btn-danger" style="margin-top: 20px">退出登录</button> </form> </div> </div> </body> </html>
head.html
<html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> </head> <body> <div th:fragment="nav"> <nav class="navbar navbar-inverse navbar-fixed-top" style="margin: 0; border: 0"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> </div> <div id="navbar" class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li> <a href="/">首页</a> </li> <li> <a href="https://anoyi.com/" target="_blank">博客</a> </li> <li> <a href="http://www.jianshu.com/u/7b7ec6f2db21" target="_blank">简书</a> </li> <li> <a href="https://github.com/ChinaSilence" target="_blank">Github</a> </li> <li> <a href="http://spring4all.com" target="_blank">源码</a> </li> </ul> <div class="navbar-form navbar-right"> <a class="btn btn-danger" th:href="@{/logout}" th:if="${#httpServletRequest.remoteUser}">退出登录</a> <a class="btn btn-success" th:href="@{/login}" th:unless="${#httpServletRequest.remoteUser}">登录</a> </div> </div> </div> </nav> </div> </body> </html>
index.html
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Spring Security Demos</title> <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> </head> <body style="padding-bottom: 0"> <div th:insert="~{header :: nav}"></div> <div class="jumbotron"> <div class="container" style="padding-top: 30px"> <h1>Spring Security</h1> <p style="margin-top: 20px;">Spring Security 是一个功能强大且可高度自定义的身份验证和访问控制框架,它是保护基于 Spring 的应用的最佳实践,与所有 Spring 项目一样,Spring Security 的真正强大之处在于它可以轻松扩展以满足自定义要求。</p> <p style="margin-top: 20px;"><a class="btn btn-primary btn-lg" href="https://spring.io/projects/spring-security" role="button"> 官方文档 »</a></p> </div> </div> <div class="container"> <!-- Example row of columns --> <div class="row"> <div class="col-md-4"> <h2>Github</h2> <p>Spring Security 源码</p> <p><a class="btn btn-default" href="https://github.com/spring-projects/spring-security" role="button">查看详情 »</a></p> </div> <div class="col-md-4"> <h2>Stackoverflow</h2> <p>Spring Security 相关问题答疑 </p> <p><a class="btn btn-default" href="https://stackoverflow.com/search?q=spring-security" role="button">查看详情 »</a></p> </div> <div class="col-md-4"> <h2>Micro</h2> <p>Spring Security 在微服务架构下的实践</p> <p><a class="btn btn-default" href="https://github.com/ChinaSilence/micro" role="button">查看详情 »</a></p> </div> </div> <hr> <footer> <p>© 2019 Power By <a href="https://anoyi.com">Anoyi</a> .</p> </footer> </div> <!-- /container --> </body> </html>
login.html
<html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>登录 | Spring Security Demos</title> <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> </head> <body style="background-color: #f1f1f1; padding-bottom: 0"> <div th:insert="~{header :: nav}"></div> <div class="container" style="margin-top: 60px"> <div class="row" style="margin-top: 100px"> <div class="col-md-6 col-md-offset-3"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title"><span class="glyphicon glyphicon-console"></span>Login</h3> </div> <div class="panel-body"> <form th:action="@{/loginUser}" method="post"> <div class="form-group" style="margin-top: 30px"> <div class="input-group col-md-6 col-md-offset-3"> <div class="input-group-addon"><span class="glyphicon glyphicon-user"></span></div> <input type="text" class="form-control" name="username" id="username" placeholder="账号"> </div> </div> <div class="form-group "> <div class="input-group col-md-6 col-md-offset-3"> <div class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></div> <input type="password" class="form-control" name="password" id="password" placeholder="密码"> </div> </div> <br> <div th:if="${param.error}"> <p style="text-align: center" class="text-danger">登录失败,账号或密码错误!</p> </div> <div th:if="${result}"> <p style="text-align: center" class="text-success" th:text="${result}"></p> </div> <div class="form-group"> <div class="input-group col-md-6 col-md-offset-3 col-xs-12 "> <button type="submit" class="btn btn-primary btn-block">登录</button> </div> </div> <div class="form-group"> <div class="input-group col-md-6 col-md-offset-3" style="text-align: center"> <a href="/register">创建账号</a> | 忘记密码? </div> </div> </form> </div> </div> </div> </div> </div> </body> </html>
效果图
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。