添加spring security

This commit is contained in:
2020-07-15 19:05:21 +08:00
parent 852c3b2b61
commit 8b04c70a07
11 changed files with 416 additions and 98 deletions
+3 -15
View File
@@ -28,10 +28,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- <dependency>
<!-- spring security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency> -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
@@ -46,16 +47,6 @@
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> -->
<!-- <dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency> -->
<!-- https://mvnrepository.com/artifact/org.json/json -->
<dependency>
<groupId>org.json</groupId>
@@ -109,9 +100,6 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
@@ -0,0 +1,98 @@
package com.yaoyuan.jiscuss.config;
import com.yaoyuan.jiscuss.handler.CustomAccessDeniedHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
prePostEnabled :决定Spring Security的前注解是否可用 [@PreAuthorize,@PostAuthorize,..]
secureEnabled : 决定是否Spring Security的保障注解 [@Secured] 是否可用
jsr250Enabled :决定 JSR-250 annotations 注解[@RolesAllowed..] 是否可用.
*/
@Configurable
@EnableWebSecurity
//开启 Spring Security 方法级安全注解 @EnableGlobalMethodSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private CustomAccessDeniedHandler customAccessDeniedHandler;
@Qualifier("userDetailServiceImpl")
@Autowired
private UserDetailsService userDetailsService;
/**
* 静态资源设置
*/
@Override
public void configure(WebSecurity webSecurity) {
//不拦截静态资源,所有用户均可访问的资源
webSecurity.ignoring().antMatchers(
"/",
"/css/**",
"/js/**",
"/images/**",
"/static/**",
"/index/**"
);
}
/**
* http请求设置
*/
@Override
public void configure(HttpSecurity http) throws Exception {
//http.csrf().disable(); //注释就是使用 csrf 功能
http.headers().frameOptions().disable();//解决 in a frame because it set 'X-Frame-Options' to 'DENY' 问题
//http.anonymous().disable();
http.authorizeRequests()
.antMatchers("/login/**","/initUserData")//不拦截登录相关方法
.permitAll()
//.antMatchers("/user").hasRole("ADMIN") // user接口只有ADMIN角色的可以访问
// .anyRequest()
// .authenticated()// 任何尚未匹配的URL只需要验证用户即可访问
.anyRequest()
.access("@rbacPermission.hasPermission(request, authentication)")//根据账号权限访问
.and()
.formLogin()
// .loginPage("/")
.loginPage("/login") //登录请求页
.loginProcessingUrl("/login") //登录POST请求路径
.usernameParameter("username") //登录用户名参数
.passwordParameter("password") //登录密码参数
.defaultSuccessUrl("/") //默认登录成功页面
.and()
.exceptionHandling()
.accessDeniedHandler(customAccessDeniedHandler) //无权限处理器
.and()
.logout()
.logoutSuccessUrl("/login?logout"); //退出登录成功URL
}
/**
* 自定义获取用户信息接口
*/
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
/**
* 密码加密算法
* @return
*/
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@@ -11,17 +11,20 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
@@ -52,6 +55,28 @@ public class UserSystemController {
List<Users> useral2 = usersService.getAllList();
logger.info(">>> 第二遍的全部用户:"+useral2);
String username ="";
//获得session对象
HttpSession session = request.getSession();
//取出session域中所有属性名
Enumeration attributeNames = session.getAttributeNames();
while (attributeNames.hasMoreElements()) {
System.out.println(attributeNames.nextElement());
}
//SPRING_SECURITY_CONTEXT
Object spring_security_context = session.getAttribute("SPRING_SECURITY_CONTEXT");
System.out.println(spring_security_context);
SecurityContext securityContext = (SecurityContext) spring_security_context;
if(securityContext!=null){
//获得认证信息
Authentication authentication = securityContext.getAuthentication();
//获得用户详情
Object principal = authentication.getPrincipal();
User user = (User) principal;
username = user.getUsername();
System.out.println(username);
}
//分页获取主题帖子
// List<Discussions> allDiscussions = discussionsService.getAllList();
@@ -64,18 +89,16 @@ public class UserSystemController {
//获取主题帖子的分页数据
List<String> pageNumList = getPageNumList(allDiscussionsPage.getTotalPages());
//获取所有标签(以后尝试去缓存中取)
List<Tags> allTags = tagsService.getAllList();
logger.info("全部标签==>{}",allTags);
HttpSession session=request.getSession();
System.out.println(userall.toString());
map.put("data", "Jiscuss 用户");
map.put("allDiscussions", allDiscussions);
map.put("pageDiscussions", pageNumList);
map.put("allTags", allTags);
map.put("username", session.getAttribute("username"));
map.put("username", username);
return "index";
}
@@ -99,25 +122,41 @@ public class UserSystemController {
//登录
@PostMapping(value = "/login")
@ResponseBody
public String login(@RequestParam("username") String username, @RequestParam("password") String password,
HttpSession session) {
JSONObject resultobj = new JSONObject();
Users user = usersService.checkByUsernameAndPassword(username, password);
if (user!=null) {
//用户名和密码完成校验
session.setAttribute("username", username); //缓存session
session.setAttribute("userid", user.getId()); //缓存session
resultobj.put("username", username);
resultobj.put("msg", "登录成功");
resultobj.put("flag", true);
} else {
//用户名和密码未完成校验
resultobj.put("msg", "用户名或密码错误");
resultobj.put("flag", false);
// @PostMapping(value = "/login")
// @ResponseBody
// public String login(@RequestParam("username") String username, @RequestParam("password") String password,
// HttpSession session) {
// JSONObject resultobj = new JSONObject();
// Users user = usersService.checkByUsernameAndPassword(username, password);
// if (user!=null) {
// //用户名和密码完成校验
// session.setAttribute("username", username); //缓存session
// session.setAttribute("userid", user.getId()); //缓存session
// resultobj.put("username", username);
// resultobj.put("msg", "登录成功");
// resultobj.put("flag", true);
// } else {
// //用户名和密码未完成校验
// resultobj.put("msg", "用户名或密码错误");
// resultobj.put("flag", false);
// }
// return resultobj.toString();
// }
//登录页
@GetMapping("/login")
public String login(@RequestParam(value = "error", required = false) String error,
@RequestParam(value = "logout", required = false) String logout,Map<String, Object> map) {
if (error != null) {
map.put("msg", "您输入的用户名密码错误!");
return "login";
}
return resultobj.toString();
if (logout != null) {
map.put("msg", "退出成功!");
return "login";
}
return "login";
}
//退出
@@ -3,7 +3,9 @@ package com.yaoyuan.jiscuss.entity;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -16,6 +18,8 @@ import javax.persistence.Table;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
@Data
@Entity
@@ -66,6 +70,4 @@ public class Users implements Serializable {
@Column(name="level")
private Integer level;
}
@@ -0,0 +1,64 @@
package com.yaoyuan.jiscuss.handler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 处理无权请求
* @author charlie
*
*/
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
private Logger log = LoggerFactory.getLogger(CustomAccessDeniedHandler.class);
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
boolean isAjax = ControllerTools.isAjaxRequest(request);
System.out.println("CustomAccessDeniedHandler handle");
if (!response.isCommitted()) {
if (isAjax) {
String msg = accessDeniedException.getMessage();
log.info("accessDeniedException.message:" + msg);
String accessDenyMsg = "{\"code\":\"403\",\"msg\":\"没有权限\"}";
ControllerTools.print(response, accessDenyMsg);
} else {
request.setAttribute(WebAttributes.ACCESS_DENIED_403, accessDeniedException);
response.setStatus(HttpStatus.FORBIDDEN.value());
RequestDispatcher dispatcher = request.getRequestDispatcher("/403");
dispatcher.forward(request, response);
}
}
}
public static class ControllerTools {
public static boolean isAjaxRequest(HttpServletRequest request) {
return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
}
public static void print(HttpServletResponse response, String msg) throws IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write(msg);
writer.flush();
writer.close();
}
}
}
@@ -0,0 +1,38 @@
package com.yaoyuan.jiscuss.handler;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* RBAC数据模型控制权限
* @author charlie
*
*/
@Component("rbacPermission")
public class RbacPermission{
private AntPathMatcher antPathMatcher = new AntPathMatcher();
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
Object principal = authentication.getPrincipal();
boolean hasPermission = false;
hasPermission = true;
// if (principal instanceof UserEntity) {
// // 读取用户所拥有的权限菜单
// List<Menu> menus = ((UserEntity) principal).getRoleMenus();
// System.out.println(menus.size());
// for (Menu menu : menus) {
// if (antPathMatcher.match(menu.getMenuUrl(), request.getRequestURI())) {
// hasPermission = true;
// break;
// }
// }
// }
return hasPermission;
}
}
@@ -0,0 +1,59 @@
package com.yaoyuan.jiscuss.service.impl;
import com.yaoyuan.jiscuss.entity.Users;
import com.yaoyuan.jiscuss.service.IUsersService;
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.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* @author yaoyuan2.chu
* @Title:
* @Package com.yaoyuan.jiscuss.service.impl
* @Description:
* @date 2020/7/15 16:34
*/
@Component
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
private IUsersService userInfoService;
/**
* 需新建配置类注册一个指定的加密方式Bean,或在下一步Security配置类中注册指定
*/
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 通过用户名从数据库获取用户信息
Users userInfo = userInfoService.getByUsername(username);
if (userInfo == null) {
throw new UsernameNotFoundException("用户不存在");
}
// 得到用户角色
String role = "admin";
// 角色集合
List<GrantedAuthority> authorities = new ArrayList<>();
// 角色必须以`ROLE_`开头,数据库中没有,则在这里加
authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
return new User(
userInfo.getUsername(),
// 因为数据库是明文,所以这里需加密密码
passwordEncoder.encode(userInfo.getPassword()),
authorities
);
}
}
+2
View File
@@ -41,6 +41,8 @@ spring:
# 设置ftl文件路径
template-loader-path:
- classpath:/templates
settings:
classic_compatible: true
# 设置静态文件路径,js,css等
mvc:
static-path-pattern: /static/**
+1 -7
View File
@@ -40,7 +40,7 @@ function loginOut() {
console.log(data);
if(data.flag){
$('#userlogin').html('');
$('#userlogin').html('<a href="javascript:void(0);" onclick="loginmodel(this)" id="loginmodel">登录 & 注册</a>');
$('#userlogin').html('<a href="/login" id="loginmodel">登录 & 注册</a>');
window.alert(username + data.msg);
username = null;
}else{
@@ -52,9 +52,3 @@ function loginOut() {
}
function loginmodel() {
$('.ui.modal.login')
.modal('show')
;
}
+2 -43
View File
@@ -30,53 +30,12 @@
<#if username??>
${username} <a href="javascript:void(0);" onclick="loginOut(this)" id="loginOut">注销</a>
<#else>
<a href="javascript:void(0);" onclick="loginmodel(this)" id="loginmodel">登录 & 注册</a>
<a href="/login" id="loginmodel">登录 & 注册</a>
</#if>
</div>
</div>
</div>
<div class="ui mini modal login">
<i class="close icon"></i>
<div class=" content">
<div class="ui middle aligned center aligned grid">
<div class="column">
<h2 class="ui blue image header">
<img src="static/assets/images/logo.png" class="image">
<div class="content">
登录到账号
</div>
</h2>
<form class="ui large form" id="loginForm">
<div class="ui stacked segment">
<div class="field">
<div class="ui left icon input">
<i class="user icon"></i>
<input type="text" name="username" placeholder="用户名">
</div>
</div>
<div class="field">
<div class="ui left icon input">
<i class="lock icon"></i>
<input type="password" name="password" placeholder="密码">
</div>
</div>
<div class="ui fluid large blue button" id="loginBtn">登录</div>
</div>
</div>
<div class="ui error message"></div>
</form>
<div class="ui message">
新用户? <a href="l#">注册</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" charset="UTF-8" src="/static/js/user/header.js"></script>
+75
View File
@@ -0,0 +1,75 @@
<!DOCTYPE html>
<html>
<title>Jiscuss Demo</title>
<head>
<!-- <link rel="stylesheet" type="text/css" href="static/semanticui/semantic.css"> -->
<!-- <script src="static/jquery/jquery-3.4.1.min.js"></script> -->
<!-- <script src="static/semanticui/semantic.min.js"></script> -->
<link crossorigin="anonymous" integrity="sha384-ATvSpJEmy1egycrmomcFxVn4Z0A6rfjwlzDQrts/1QRerQhR9EEpEYtdysLpQPuQ" href="https://lib.baomitu.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="/static/semanticui/my.css">
<script crossorigin="anonymous" integrity="sha384-vk5WoKIaW/vJyUAd9n/wmopsmNhiy+L2Z+SBxGYnUkunIxVxAv/UtMOhba/xskxh" src="https://lib.baomitu.com/jquery/3.4.1/jquery.min.js"></script>
<script crossorigin="anonymous" integrity="sha384-6urqf2sgCGDfIXcoxTUOVIoQV+jFr/Zuc4O2wCRS6Rnd8w0OJ17C4Oo3PuXu8ZtF" src="https://lib.baomitu.com/semantic-ui/2.4.1/semantic.min.js"></script>
<script crossorigin="anonymous" integrity="sha384-CpsBIlOAWHuSRRN235sCBzEeKN6hLT6SpOGRkGadKpYj0gDP7+s3Q8pC38z8uGHH" src="https://lib.baomitu.com/tinymce/5.1.1/tinymce.min.js"></script>
<style type="text/css">
body {
background-color: #DADADA;
}
body > .grid {
height: 100%;
}
.image {
margin-top: -100px;
}
.column {
max-width: 450px;
}
</style>
</head>
<body>
<div class="ui middle aligned center aligned grid">
<div class="column">
<h2 class="ui blue image header">
<img src="static/assets/images/logo.png" class="image">
<div class="content">
登录到账号
</div>
</h2>
<form class="ui large form" action="/login" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<div class="ui stacked segment">
<div class="field">
<div class="ui left icon input">
<i class="user icon"></i>
<input type="text" name="username" placeholder="用户名">
</div>
</div>
<div class="field">
<div class="ui left icon input">
<i class="lock icon"></i>
<input type="password" name="password" placeholder="密码">
</div>
</div>
<div class="field">
<div class="ui pointing red basic label">
${msg}
</div>
</div>
<button class="ui fluid large blue button" type="submit">登录</button>
</div>
<div class="ui error message"></div>
</form>
<div class="ui message">
<a href="/"><——Jiscuss首页</a> 新用户? <a href="login.php#">注册</a>
</div>
</div>
</div>
</body>
</html>