绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
pac4j - Java 权限引擎
2019-10-15 09:50:49

什么是PAC4J?

pac4j是一个简单而强大的安全引擎,用于Java对用户进行身份验证、获取其配置文件和管理授权,以确保web应用程序安全。它提供了一套完整的概念和组件。它基于Java 8,并在Apache 2许可下使用。它可用于大多数框架/工具和支持大多数认证/授权机制。

已经集成可用的场景

J2E • Spring Web MVC (Spring Boot) • Spring Security (Spring Boot) • Apache Shiro

Play 2.x • Vertx • Spark Java • Ratpack • Undertow

CAS server • JAX-RS • Dropwizard • Apache Knox • Jooby

身份验证机制

OAuth (Facebook, Twitter, Google…) - SAML - CAS - OpenID Connect - HTTP - OpenID - Google App Engine - Kerberos (SPNEGO/Negotiate)

LDAP - SQL - JWT - MongoDB - CouchDB - IP address - REST API

授权机制

Roles/permissions - Anonymous/remember-me/(fully) authenticated - Profile type, attribute

CORS - CSRF - Security headers - IP address, HTTP method

PAC4J基本上基于jdk1.8的环境,由于公司电脑的jdk是1.7,这里就简单的集成CAS就行,其他的认证方式等回家再写,家里的电脑jdk是1.8的。

项目结构

pom.xml

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

org.ikane

demo

0.0.1-SNAPSHOT

jar

spring-boot-pac4j-demo

Spring-boot PAC4J DEMO

org.springframework.boot

spring-boot-starter-parent

1.2.8.RELEASE

UTF-8

1.7

org.springframework.boot

spring-boot-starter-thymeleaf

org.springframework.boot

spring-boot-starter-web

org.thymeleaf.extras

thymeleaf-extras-springsecurity4

2.1.2.RELEASE

org.springframework.boot

spring-boot-starter-security

org.pac4j

spring-security-pac4j

1.4.1

org.springframework.security

spring-security-web

org.springframework.security

spring-security-config

org.pac4j

pac4j-cas

1.8.5

org.springframework.boot

spring-boot-starter-test

test

org.springframework.boot

spring-boot-maven-plugin

Pac4jConfig.java

package org.ikane;

import org.ikane.security.ClientUserDetailsService;

import org.ikane.service.AccountService;

import org.pac4j.cas.client.CasClient;

import org.pac4j.core.client.Clients;

import org.pac4j.springframework.security.authentication.ClientAuthenticationProvider;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class Pac4jConfig {

public static String CAS_LOGIN_URL = "https://casserverpac4j.herokuapp.com/login";

@Value("${oauth.callback.url}")

private String oauthCallbackUrl;

@Autowired

AccountService accountService;

@Bean

CasClient casClient() {

return new CasClient(CAS_LOGIN_URL);

}

@Bean

Clients clients() {

return new Clients(oauthCallbackUrl, casClient());

}

@Bean

ClientUserDetailsService clientUserDetailsService() {

ClientUserDetailsService clientUserDetailsService = new ClientUserDetailsService();

clientUserDetailsService.setAccountService(accountService);

return clientUserDetailsService;

}

@Bean

ClientAuthenticationProvider clientProvider() {

ClientAuthenticationProvider clientAuthenticationProvider = new ClientAuthenticationProvider();

clientAuthenticationProvider.setClients(clients());

clientAuthenticationProvider.setUserDetailsService(clientUserDetailsService());

return clientAuthenticationProvider;

}

}

SecurityConfig.java

package org.ikane;

import org.pac4j.core.client.Clients;

import org.pac4j.springframework.security.authentication.ClientAuthenticationProvider;

import org.pac4j.springframework.security.web.ClientAuthenticationFilter;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.ApplicationContext;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.security.authentication.AuthenticationManager;

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.web.authentication.UsernamePasswordAuthenticationFilter;

import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;

import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;

@Configuration

@EnableWebSecurity

@EnableGlobalMethodSecurity(prePostEnabled=true, securedEnabled=true)

public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired

ApplicationContext context;

@Autowired

Clients clients;

@Autowired

ClientAuthenticationProvider clientProvider;

@Override

public void configure(WebSecurity web) throws Exception {

web

.ignoring()

.antMatchers(

"/**/*.css",

"/**/*.png",

"/**/*.gif",

"/**/*.jpg",

"/**/*.ico",

"/**/*.js"

);

}

@Override

protected void configure(HttpSecurity http) throws Exception {

http

.csrf().disable()

.authorizeRequests()

.and()

.formLogin()

.loginPage("/login")

.permitAll()

.and()

.logout()

.logoutUrl("/logout")

.logoutSuccessUrl("/")

.permitAll()

;

http.addFilterBefore(clientFilter(), UsernamePasswordAuthenticationFilter.class);

}

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

super.configure(auth);

auth.authenticationProvider(clientProvider);

}

ClientAuthenticationFilter clientFilter() {

String suffixUrl="/*";

ClientAuthenticationFilter clientAuthenticationFilter = new ClientAuthenticationFilter(suffixUrl);

clientAuthenticationFilter.setClients(clients);

clientAuthenticationFilter.setSessionAuthenticationStrategy(sas());

//clientAuthenticationFilter.setAuthenticationManager((AuthenticationManager)clientProvider);

return clientAuthenticationFilter;

/*

return new ClientAuthenticationFilter(

clients: clients,

sessionAuthenticationStrategy: sas(),

authenticationManager: clientProvider as AuthenticationManager

)

*/

}

@Bean

SessionAuthenticationStrategy sas() {

return new SessionFixationProtectionStrategy();

}

}

SpringBootPac4jDemoApplication.java

package org.ikane;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication

public class SpringBootPac4jDemoApplication {

public static void main(String[] args) {

SpringApplication.run(SpringBootPac4jDemoApplication.class, args);

}

}

ThymeleafConfig.java

package org.ikane;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect;

@Configuration

public class ThymeleafConfig {

@Bean

public SpringSecurityDialect springSecurityDialect() {

return new SpringSecurityDialect();

}

}

IndexController.java

package org.ikane.controller;

import org.springframework.security.access.prepost.PreAuthorize;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

@Controller

class IndexController {

@RequestMapping("/")

@PreAuthorize("isAuthenticated()")

public String index() {

return "index";

}

}

LoginController.java

package org.ikane.controller;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.pac4j.cas.client.CasClient;

import org.pac4j.core.client.BaseClient;

import org.pac4j.core.client.Clients;

import org.pac4j.core.context.J2EContext;

import org.pac4j.core.context.WebContext;

import org.pac4j.core.exception.RequiresHttpAction;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.security.authentication.AnonymousAuthenticationToken;

import org.springframework.security.core.Authentication;

import org.springframework.security.core.context.SecurityContextHolder;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

@Controller

public class LoginController {

Logger logger = LoggerFactory.getLogger(LoginController.class);

@Autowired

private Clients clients;

@RequestMapping("/login")

public String login(HttpServletRequest request, HttpServletResponse response, Model model) {

if (isAuthenticated()) {

return "redirect:/";

}

final WebContext context = new J2EContext(request, response);

//定义cas客户端

final CasClient casClient = (CasClient) clients.findClient(CasClient.class);

model.addAttribute("casAuthUrl", getClientLocation(casClient, context));

return "login";

}

//获取客户端的链接

public String getClientLocation(BaseClient client, WebContext context) {

try {

return ((CasClient)client).getRedirectAction(context, false).getLocation();

} catch (RequiresHttpAction e) {

e.printStackTrace();

logger.error("error", e);

return null;

}

//return client.getRedirectAction(context, false, false).getLocation();

}

protected boolean isAuthenticated() {

Authentication auth = SecurityContextHolder.getContext().getAuthentication();

return !(auth instanceof AnonymousAuthenticationToken);

}

}

ClientUserDetails.java

package org.ikane.security;

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.userdetails.UserDetails;

class ClientUserDetails implements UserDetails {

private static final long serialVersionUID = 6523314653561682296L;

String username;

String providerId;

Collection authorities;

String password;

public ClientUserDetails() {

// TODO Auto-generated constructor stub

}

public ClientUserDetails(String username, String providerId, Collection authorities) {

super();

this.username = username;

this.providerId = providerId;

this.authorities = authorities;

}

@Override

public Collection getAuthorities() {

return authorities;

}

@Override

public String getPassword() {

return password;

}

@Override

public String getUsername() {

// TODO Auto-generated method stub

return username;

}

@Override

public boolean isAccountNonExpired() {

return true;

}

@Override

public boolean isAccountNonLocked() {

return false;

}

@Override

public boolean isCredentialsNonExpired() {

return true;

}

@Override

public boolean isEnabled() {

return true;

}

public String getProviderId() {

return providerId;

}

public void setProviderId(String providerId) {

this.providerId = providerId;

}

public void setUsername(String username) {

this.username = username;

}

public void setAuthorities(Collection authorities) {

this.authorities = authorities;

}

public void setPassword(String password) {

this.password = password;

}

}

ClientUserDetailsService.java

package org.ikane.security;

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

import org.ikane.service.AccountService;

import org.pac4j.springframework.security.authentication.ClientAuthenticationToken;

import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.authority.SimpleGrantedAuthority;

import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;

import org.springframework.security.core.userdetails.UserDetails;

import org.springframework.security.core.userdetails.UsernameNotFoundException;

public class ClientUserDetailsService implements AuthenticationUserDetailsService {

private AccountService accountService;

public UserDetails loadUserDetails(final ClientAuthenticationToken token) throws UsernameNotFoundException {

Map account = accountService.lookupAccountByProvider(token.getClientName(), token.getUserProfile().getId());

//String username = account.containsKey("displayName") ? account.displayName : ""

String username = "admin";

final List authorities = new ArrayList();

for (String role: token.getUserProfile().getRoles()) {

authorities.add(new SimpleGrantedAuthority(role));

}

if (!account.isEmpty() && authorities.isEmpty()) {

// default to user role

authorities.add(new SimpleGrantedAuthority("ROLE_USER"));

}

return new ClientUserDetails(username, token.getUserProfile().getId(), authorities);

}

public AccountService getAccountService() {

return accountService;

}

public void setAccountService(AccountService accountService) {

this.accountService = accountService;

}

}

AccountService.java

package org.ikane.service;

import java.util.HashMap;

import java.util.Map;

import org.springframework.stereotype.Service;

@Service

public class AccountService {

/**

* @Autowired

JdbcTemplate jdbcTemplate

* */

public Map lookupAccountByProvider(String providerName, String providerUserId) {

HashMap map = new HashMap ();

/**

*

List results = jdbcTemplate.query(

"select * from account where provider = ? and provider_user_id = ?",

[providerName, providerUserId] as Object[],

new GenericRowMapper()

)

if (results.size() > 1) {

throw new Exception("multiple accounts by provider [${providerName}] for id [${providerUserId}]")

}

* **/

return map;

}

public Boolean createAccountForProvider(String providerName, String providerUserId, String displayName) {

/**

* log.debug("creating new account for displayName=${displayName} using provider=${providerName} with id ${providerUserId}")

int result = jdbcTemplate.update(

"insert into account (display_name, provider, provider_user_id) values (?, ?, ?)",

displayName,

providerName,

providerUserId

)

if (result != 1) {

log.warn("creation of account for provider [${providerName}] and id [${providerUserId}] failed")

return false

}

* */

return true;

}

}

index.html

Index Page sgdsfg

login.html

xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"

layout:decorator="layouts/default">

分享好友

分享这个小栈给你的朋友们,一起进步吧。

应用开发
创建时间:2020-06-17 15:31:04
应用软件开发是指使用程序语言C#、java、 c++、vb等语言编写,主要是用于商业、生活应用的软件的开发。
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

技术专家

查看更多
  • 栈栈
    专家
戳我,来吐槽~